参考: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. vs2008中如何在项目属性中切换“图形形式”和“控制台形式”——即调出或消除黑窗口
  2. 08 | 事务到底是隔离的还是不隔离的
  3. 【机器视觉】 convert_vector_to_tuple算子
  4. 在副业刚需的时代,如何掌握副业的正确姿势?
  5. html css position,[CSS]CSS Position 详解
  6. 虚拟化顶级技术会议KVM Forum演讲分享 | 移动云KVM共享云盘技术实践
  7. linux下用脚本语言开发自动重启程序
  8. 我的idea突然没有SVN了是怎么回事
  9. idea配置Translation插件为有道翻译引擎
  10. git 提交代码防止尾行序列LF转为CRLF
  11. utility restore.php,PHP实现短网址还原实例代码
  12. 【倾心整理】高级工程师手写总结,入门到顶级程序员的学习方法
  13. 计算机word考试试题学做,全国计算机等级考试Word试题及答案
  14. 面向對象在VB6語言中的應用
  15. 《途客圈创业记:不疯魔,不成活》一一1.5 依依辞别Juniper
  16. 高数-极限-存在与连续1
  17. 剪视频到底要什么样的电脑配置?
  18. 【转】分享 Visa 问题准备
  19. 轻松学习JavaScript十一:JavaScript基本类型(包含类型转换)和引用类型
  20. 【基于C#的图书借阅管理系统(ASP.NET)】

热门文章

  1. unity水特效与标准资源包的下载导入
  2. 微信域名防封技术,微信域名总是被屏蔽要怎么解决
  3. 【认知计算】Deepfake/Anti-deepfake综述探究
  4. SharePoint Online:软件边界和限制
  5. LaTeX中的保留字符(反斜杠)和特殊符号(角度、摄氏度)怎么表示?
  6. 诺基亚symbian 手册汇编
  7. 比 Elasticsearch 更快!RediSearch + RedisJSON = 王炸
  8. 桌面计算机1008桌面计算机,windows桌面精灵
  9. python爬楼梯问题_用Python解决经典的爬楼梯问题
  10. 【题解】P3975 [TJOI2015]弦论 后缀自动机