使用OAuth2的SSO分析
参考: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分析相关推荐
- 使用Spring Secuirty Oauth2实现SSO单点登录
文章目录 1. 什么是单点登录 2. 微服务架构下单点登录的思路 3. 使用 Spring Secuirty Oauth2 实现SSO单点登录 ①:建表 ②:授权服务器逻辑 ③:网关逻辑 4. 接口测 ...
- 前后端分离基于Oauth2的SSO单点登录怎样做?
一.说明 单点登录顾名思义就是在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统,免除多次登录的烦恼:本文主要介绍跨域间的 前后端分离 项目怎样实现单点登录,并且与 非前后端分离 的差 ...
- java oauth sso 源码_基于Spring Security Oauth2的SSO单点登录+JWT权限控制实践
概 述 在前文<基于Spring Security和 JWT的权限系统设计>之中已经讨论过基于 Spring Security和 JWT的权限系统用法和实践,本文则进一步实践一下基于 Sp ...
- oauth2 ldap sso
ldap可以理解为一个统一存储用户信息,组织架构信息等等的数据库,并提供登录认证手段,但不提供sso,token等机制,用户登录是明文 openldap介绍和使用 - 云+社区 - 腾讯云 sso: ...
- 16 OAuth2登录流程分析
上文已经简单说过,OAuth2登录的基本实现原理是:Client获取用户授权,得到令牌,通过令牌,获取用户信息(资源).再在本地构建用户登录认证信息,维持用户会话状态,以此达到登录的目的. 下文便从源 ...
- 基于Spring Security + OAuth2 的SSO单点登录(服务端)
相关技术 spring security: 用于安全控制的权限框架 OAuth2: 用于第三方登录认证授权的协议 JWT:客户端和服务端通信的数据载体 传统登录 登录web系统后将用户信息保存在ses ...
- 基于Spring Security OAuth2的SSO(单点登录)
1. Theories What is SSO? 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一.SSO的定义是在多个应用系统中,用户只需要登录一 ...
- JWT实战 Spring Security Oauth2整合JWT 整合SSO单点登录
文章目录 一.JWT 1.1 什么是JWT 1.2 JWT组成 头部(header) 载荷(payload) 签名(signature) 如何应用 1.3 JJWT 快速开始 创建token toke ...
- Spring Security、oauth2、单点登陆SSO的关系
文章目录 概述 1. 什么是Spring Security 1.1 配置示例 1.2 spring security 基本原理 1.2 Spring Security存在的问题 2. 什么是oauth ...
最新文章
- Python Django 数据缓存存储位置类
- 《统计学》学习笔记之数据的收集
- 为什么C语言函数不能返回数组,却可以返回结构体
- 过去式加ed的发音_【思语小课堂】时态二三事:规则动词过去式的发音规则
- 支付宝这些程序员要逆天,滑板、画漫画、写科幻小说、航拍,玩得太溜
- 创业公司是如何进行研发管理和绩效考核的?
- 漫步最优化十三——驻点
- Java中的变量分类_开发简单的Java应用
- 同一列两行数据怎么合并成一行_经常加班怎么办?两分钟学会这4招,让同事刮目相看!...
- pve安装黑群晖直通硬盘_更新教程:群晖下直接挂载WINDOWS的NTFS格式硬盘,试验通过......
- Python爬虫之爬取豆瓣图书TOP250
- 田野调查手记·浮山摩崖石刻(七)
- Ubuntu连接不上网络问题的解决方法
- 前端新手遇到的问题 ---含“ 文字贯穿线、@font-face、axios ”知识
- Java开发必读--初识微服务一定要阅读这篇文章
- 眼球追踪技术给各大科技巨头带来的四大应用前景
- Android Protobuf应用及原理
- nginx+rtmp+OBS搭建音视频直播服务
- vue使用Echarts画柱状图
- 远程工作:从300到300万,一个远程外包团队的发展历程和经验【转载】
热门文章
- 行为识别:让机器学会“察言观色”第一步
- 如何使用mysql数据库做网站_php小型数据库(不用mysql做网站)
- 人脸表情识别系统介绍——上篇(python实现,含UI界面及完整代码)
- 斩获数亿元B轮融资,这家Tier 1抢跑「L2/L2+」主战场
- SQLServer修改表数据
- tomcat开启远程调试功能
- 计算机的知识体系结构,计算机体系结构知识点
- jupyter添加标题、文字注释;
- win10 无法删除 注册表 蓝牙_编辑设置Win10删除蓝牙设备后无法重新添加的修复办法...
- EPUB转为PDF和书籍pdf下载