参考:https://github.com/spring-guides/tut-spring-security-and-angular-js/blob/master/oauth2-vanilla/README.adoc

1.浏览器向UI服务器点击触发要求安全认证
2.跳转到授权服务器获取授权许可码
3.从授权服务器带授权许可码跳回来
4.UI服务器向授权服务器获取AccessToken
5.返回AccessToken到UI服务器
6.发出/resource请求到UI服务器
7.UI服务器将/resource请求转发到Resource服务器
8.Resource服务器要求安全验证,于是直接从授权服务器获取认证授权信息进行判断后(最后会响应给UI服务器,UI服务器再响应给浏览中器)

一.先创建OAuth2授权服务器
1.使用Spring Initializrt生成初始项目,选使用spring boot 1.3.3生成maven项目,根据需要填写group,artifact,依赖选Web和Security两块,点生成按钮即可.
2.加入OAuth2依赖到pom.xml

<dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId>
</dependency>

修改主类(这里同时也作为资源服务器).

@SpringBootApplication
@RestController
@EnableAuthorizationServer
@EnableResourceServer
public class AuthserverApplication {@RequestMapping("/user")public Principal user(Principal user) {return user;}public static void main(String[] args) {SpringApplication.run(AuthserverApplication.class, args);}}

同时修改servlet容器的port,contextPath,注册一个测试用户与客户端,加入配置:application.properties

server.port: 9999
server.contextPath: /uaa
security.user.password: password
security.sessions: if-required
security.oauth2.client.clientId: acme
security.oauth2.client.clientSecret: acmesecret
security.oauth2.client.authorized-grant-types: authorization_code,refresh_token,password
security.oauth2.client.scope: openid

基于spring boot的security的session创建策略默认是STATELESS,至于几个选项意义,可看org.springframework.security.config.http.SessionCreationPolicy
启动授权服务器后,可测试了:
a.打开浏览器输入地址http://localhost:9999/uaa/oauth/authorize?response_type=code&client_id=acme&redirect_uri=http://example.com发出请求,然后根据以上配置,输入用户名/密码,点同意,获取返回的授权许可码
b.在linux的bash或mac的terminal输入
[root@dev ~]#curl acme:acmesecret@192.168.1.115:9999/uaa/oauth/token \
-d grant_type=authorization_code -d client_id=acme \
-d redirect_uri=http://example.com -d code=fjRdsL
回车获取access token,其中fjRdsL替换上步获取的授权许可码.返回结果类似如下:
{"access_token":"8eded27d-b849-4473-8b2d-49ae49e17943","token_type":"bearer","refresh_token":"5e9af75c-c442-433f-81ba-996eb2c00f53","expires_in":43199,"scope":"openid"}
从返回结果复制access_token,继续:
[root@dev ~]# TOKEN=8eded27d-b849-4473-8b2d-49ae49e17943
[root@dev ~]# curl -H “Authorization: Bearer $TOKEN” 192.168.1.115:9999/uaa/user
其中上面的8eded27d-b849-4473-8b2d-49ae49e17943是access_token,根据实际情况替换,第二个命令返回结果类似如下:
{"details":{"remoteAddress":"192.168.1.194","sessionId":null,"tokenValue":"8eded27d-b849-4473-8b2d-49ae49e17943","tokenType":"Bearer","decodedDetails":null},"authorities":[{"authority":"ROLE_USER"}],"authenticated":true,"userAuthentication":{"details":{"remoteAddress":"0:0:0:0:0:0:0:1","sessionId":"3943F6861E0FE31C29568542730342F6"},"authorities":[{"authority":"ROLE_USER"}],"authenticated":true,"principal":{"password":null,"username":"user","authorities":[{"authority":"ROLE_USER"}],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true,"enabled":true},"credentials":null,"name":"user"},"oauth2Request":{"clientId":"acme","scope":["openid"],"requestParameters":{"response_type":"code","redirect_uri":"http://example.com","code":"QzbdLe","grant_type":"authorization_code","client_id":"acme"},"resourceIds":[],"authorities":[{"authority":"ROLE_USER"}],"approved":true,"refresh":false,"redirectUri":"http://example.com","responseTypes":["code"],"extensions":{},"grantType":"authorization_code","refreshTokenRequest":null},"credentials":"","principal":{"password":null,"username":"user","authorities":[{"authority":"ROLE_USER"}],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true,"enabled":true},"clientOnly":false,"name":"user"}
从结果来看,使用access token访问资源一切正常,说明授权服务器没问题.

二.再看分离的资源服务器(改动也不少)
不再使用SpringSession从redis抽取认证授权信息,而是使用ResourceServerTokenServices向授权服务器发送请求获取认证授权信息.因些没用到SpringSession时可移除,同时application.properties配置security.oauth2.resource.userInfoUri或security.oauth2.resource.tokenInfoUri中的一个,主类修改如下:

@SpringBootApplication
@RestController
@EnableResourceServer
public class ResourceApplication {@RequestMapping("/")public Message home() {return new Message("Hello World");}public static void main(String[] args) {SpringApplication.run(ResourceApplication.class, args);}
}

最后运行主类的main方法,开始测试(授权服务器前面启动了,access_token也得到了),于是在使用curl命令:
[root@dev ~]# curl -H “Authorization: Bearer $TOKEN” 192.168.1.115:9000
返回结果类似如下:
{"id":"03af8be3-2fc3-4d75-acf7-c484d9cf32b1","content":"Hello World"}
可借鉴的经验,我在windows上开发,启动资源服务器,然后资源服务器有配置server.address: 127.0.0.1,这里限制容器只能是本机访问,如果使用局域网IP是不可以访问的,比如你在别人的机器或在一台虚拟的linux上使用curl都是不是访问的,注释这行配置,这限制就解除.
跟踪下获取认证授权的信息过程:
1.userInfoRestTemplate Bean的声明在org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration.UserInfoRestTemplateConfiguration#userInfoRestTemplate
2.使用前面配置的userInfoUri和上面的userInfoRestTemplate Bean在org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration.RemoteTokenServicesConfiguration.UserInfoTokenServicesConfiguration#userInfoTokenServices创建UserInfoTokenServices Bean.
3.在org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer#configure添加了org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter
4.当使用curl -H “Authorization: Bearer $TOKEN” 192.168.1.115:9000发出请求时,直到被OAuth2AuthenticationProcessingFilter拦截器处理,
org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter#doFilter{
Authentication authentication = tokenExtractor.extract(request);//抽取Token
Authentication authResult = authenticationManager.authenticate(authentication);//还原解码认证授权信息
}
org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager#authenticate{
OAuth2Authentication auth = tokenServices.loadAuthentication(token);//这里的tokenServices就是上面的UserInfoTokenServices Bean,就在这里向授权服务器发出请求.
}
三.UI服务器作为SSO的客户端.
1.同样UI服务器不需要springSession,认证如我们所期望的,交给授权服务器,所以使用Spring Security OAuth2依赖替换Spring Session和Redis依赖.
2.当然UI服务器还是API网关的角色,所以不要移除@EnableZuulProxy.在UI服务器主类加上@EnableOAuth2Sso,这个注解会帮我们完成跳转到授权服务器,当然要些配置application.yml

zuul:routes:resource:path: /resource/**url: http://localhost:9000user:path: /user/**url: http://localhost:9999/uaa/user

这里将”/user”请求代理到授权服务器
3.继续修改UI主类继承WebSecurityConfigurerAdapter,重写org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#configure(org.springframework.security.config.annotation.web.builders.HttpSecurity)
目的是为了修改@EnableOAuth2Sso引起的默认Filter链,默认是org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2SsoDefaultConfiguration#configure,这个类上面有@Conditional(NeedsWebSecurityCondition.class)意思应该是,没有WebSecurityConfigurerAdapter才会去执行这个config,因为继承了这个类,所以此config不再执行.
4.作为oauth2的客户端,application.yml下面这几项是少不了的

security:oauth2:client:accessTokenUri: http://localhost:9999/uaa/oauth/tokenuserAuthorizationUri: http://localhost:9999/uaa/oauth/authorizeclientId: acmeclientSecret: acmesecretresource:userInfoUri: http://localhost:9999/uaa/user

最后一项,因为也作为资源服务器,所以也加上吧.

spring:aop:proxy-target-class: true

spring aop默认一般都是使用jdk生成代理,前提是要有接口,cglib生成代理,目标类不能是final类,这是最基本的条件.估计是那些restTemplate没有实现接口,所以不得不在这里使用cglib生成代理.
5.其它的前端微小改变,这里不赘述.把授权服务器,分离的资源服务器和这个UI服务器都启动.准备测试:http://localhost:8080/login
a.经过security的拦截链接中的org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter.doFilter拦截,触发了attemptAuthentication方法

    public OAuth2AccessToken getAccessToken() throws UserRedirectRequiredException {OAuth2AccessToken accessToken = context.getAccessToken();if (accessToken == null || accessToken.isExpired()) {try {accessToken = acquireAccessToken(context);}catch (UserRedirectRequiredException e) {context.setAccessToken(null); // No point hanging onto it nowaccessToken = null;String stateKey = e.getStateKey();if (stateKey != null) {Object stateToPreserve = e.getStateToPreserve();if (stateToPreserve == null) {stateToPreserve = "NONE";}context.setPreservedState(stateKey, stateToPreserve);}throw e;}}return accessToken;}

acquireAccessToken(context)去获取token的时候触发抛异常.在org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider#getRedirectForAuthorization处理发送的url,最后这个UserRedirectRequiredException往上抛
一直往上抛到org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter#doFilter

    catch (Exception ex) {// Try to extract a SpringSecurityException from the stacktraceThrowable[] causeChain = throwableAnalyzer.determineCauseChain(ex);UserRedirectRequiredException redirect = (UserRedirectRequiredException) throwableAnalyzer.getFirstThrowableOfType(UserRedirectRequiredException.class, causeChain);if (redirect != null) {redirectUser(redirect, request, response);} else {if (ex instanceof ServletException) {throw (ServletException) ex;}if (ex instanceof RuntimeException) {throw (RuntimeException) ex;}throw new NestedServletException("Unhandled exception", ex);}}

终于看到redirectUser(redirect, request, response);进行跳转到授权服务器去了.

授权服务器跳回到UI服务器原来的地址(带回来授权许可码),再次被OAuth2ClientAuthenticationProcessingFilter拦截发送获取accessToken,经org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport#retrieveToken提交POST请求,获取到返回原来发请求处得到OAuth2AccessToken对象.
在org.springframework.security.oauth2.client.OAuth2RestTemplate#acquireAccessToken使用oauth2Context.setAccessToken(accessToken);对token进行保存.有了accessToken,就可以从授权服务器获取用户信息了.

最后,当用户点logout的时候,授权服务器根本没有退出(销毁认证授权信息)

使用OAuth2的SSO分析相关推荐

  1. 使用Spring Secuirty Oauth2实现SSO单点登录

    文章目录 1. 什么是单点登录 2. 微服务架构下单点登录的思路 3. 使用 Spring Secuirty Oauth2 实现SSO单点登录 ①:建表 ②:授权服务器逻辑 ③:网关逻辑 4. 接口测 ...

  2. 前后端分离基于Oauth2的SSO单点登录怎样做?

    一.说明 单点登录顾名思义就是在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统,免除多次登录的烦恼:本文主要介绍跨域间的 前后端分离 项目怎样实现单点登录,并且与 非前后端分离 的差 ...

  3. java oauth sso 源码_基于Spring Security Oauth2的SSO单点登录+JWT权限控制实践

    概 述 在前文<基于Spring Security和 JWT的权限系统设计>之中已经讨论过基于 Spring Security和 JWT的权限系统用法和实践,本文则进一步实践一下基于 Sp ...

  4. oauth2 ldap sso

    ldap可以理解为一个统一存储用户信息,组织架构信息等等的数据库,并提供登录认证手段,但不提供sso,token等机制,用户登录是明文 openldap介绍和使用 - 云+社区 - 腾讯云 sso: ...

  5. 16 OAuth2登录流程分析

    上文已经简单说过,OAuth2登录的基本实现原理是:Client获取用户授权,得到令牌,通过令牌,获取用户信息(资源).再在本地构建用户登录认证信息,维持用户会话状态,以此达到登录的目的. 下文便从源 ...

  6. 基于Spring Security + OAuth2 的SSO单点登录(服务端)

    相关技术 spring security: 用于安全控制的权限框架 OAuth2: 用于第三方登录认证授权的协议 JWT:客户端和服务端通信的数据载体 传统登录 登录web系统后将用户信息保存在ses ...

  7. 基于Spring Security OAuth2的SSO(单点登录)

    1. Theories What is SSO? 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一.SSO的定义是在多个应用系统中,用户只需要登录一 ...

  8. JWT实战 Spring Security Oauth2整合JWT 整合SSO单点登录

    文章目录 一.JWT 1.1 什么是JWT 1.2 JWT组成 头部(header) 载荷(payload) 签名(signature) 如何应用 1.3 JJWT 快速开始 创建token toke ...

  9. Spring Security、oauth2、单点登陆SSO的关系

    文章目录 概述 1. 什么是Spring Security 1.1 配置示例 1.2 spring security 基本原理 1.2 Spring Security存在的问题 2. 什么是oauth ...

最新文章

  1. Python Django 数据缓存存储位置类
  2. 《统计学》学习笔记之数据的收集
  3. 为什么C语言函数不能返回数组,却可以返回结构体
  4. 过去式加ed的发音_【思语小课堂】时态二三事:规则动词过去式的发音规则
  5. 支付宝这些程序员要逆天,滑板、画漫画、写科幻小说、航拍,玩得太溜
  6. 创业公司是如何进行研发管理和绩效考核的?
  7. 漫步最优化十三——驻点
  8. Java中的变量分类_开发简单的Java应用
  9. 同一列两行数据怎么合并成一行_经常加班怎么办?两分钟学会这4招,让同事刮目相看!...
  10. pve安装黑群晖直通硬盘_更新教程:群晖下直接挂载WINDOWS的NTFS格式硬盘,试验通过......
  11. Python爬虫之爬取豆瓣图书TOP250
  12. 田野调查手记·浮山摩崖石刻(七)
  13. Ubuntu连接不上网络问题的解决方法
  14. 前端新手遇到的问题 ---含“ 文字贯穿线、@font-face、axios ”知识
  15. Java开发必读--初识微服务一定要阅读这篇文章
  16. 眼球追踪技术给各大科技巨头带来的四大应用前景
  17. Android Protobuf应用及原理
  18. nginx+rtmp+OBS搭建音视频直播服务
  19. vue使用Echarts画柱状图
  20. 远程工作:从300到300万,一个远程外包团队的发展历程和经验【转载】

热门文章

  1. 行为识别:让机器学会“察言观色”第一步
  2. 如何使用mysql数据库做网站_php小型数据库(不用mysql做网站)
  3. 人脸表情识别系统介绍——上篇(python实现,含UI界面及完整代码)
  4. 斩获数亿元B轮融资,这家Tier 1抢跑「L2/L2+」主战场
  5. SQLServer修改表数据
  6. tomcat开启远程调试功能
  7. 计算机的知识体系结构,计算机体系结构知识点
  8. jupyter添加标题、文字注释;
  9. win10 无法删除 注册表 蓝牙_编辑设置Win10删除蓝牙设备后无法重新添加的修复办法...
  10. EPUB转为PDF和书籍pdf下载