参考: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:/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--8b2d-49ae49e17943
[root@dev ~]# curl -H “Authorization: Bearer $TOKEN” 192.168.1.115:/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访问资源一切正常,说明授权服务器没问题.

二.再看分离的资源服务器(改动也不少)

不再使用Spring Session从Redis抽取认证授权信息,而是使用ResourceServerTokenServices向授权服务器发送请求获取认证授权信息.
因些没用到Spring Session时可移除,同时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: 

返回结果类似如下:

{
"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服务器不需要Spring Session,认证如我们所期望的,交给授权服务器,所以使用Spring Security OAuth2依赖替换Spring Session和Redis依赖
2.当然UI服务器还是API网关的角色,所以不要移除@EnableZuulProxy
在UI服务器主类加上@EnableOAuth2Sso,这个注解会帮我们完成跳转到授权服务器,当然要些配置application.yml

zuul:
routes:
resource:
path: /resource/**
url: http://localhost:9000
user:
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/token
userAuthorizationUri: http://localhost:9999/uaa/oauth/authorize
clientId: acme
clientSecret: acmesecret
resource:
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 now
accessToken = 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 stacktrace
Throwable[] 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的时候,授权服务器根本没有退出(销毁认证授权信息)

http://blog.csdn.net/xiejx618/article/details/51039653

使用OAuth2的SSO分析的更多相关文章

  1. 使用JWT的OAuth2的SSO分析

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

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

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

  3. JWT、OAUTH2与SSO资料补充

    JWT: 阮一峰:http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html https://blog.csdn.net/q ...

  4. Spring Security基于Oauth2的SSO单点登录怎样做?一个注解搞定

    一.说明 单点登录顾名思义就是在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统,免除多次登录的烦恼.本文主要介绍 同域 和 跨域 两种不同场景单点登录的实现原理,并使用 Spring ...

  5. Spring Security OAuth2 源码分析

    Spring Security OAuth2 主要两部分功能:1.生成token,2.验证token,最大概的流程进行了一次梳理 1.Server端生成token (post /oauth/token ...

  6. spring oauth2相关资料

    理解OAuth 2.0  *****http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html Secure REST API with oauth2 ...

  7. 【SpringSecurityOAuth2】源码分析@EnableOAuth2Sso在Spring Security OAuth2 SSO单点登录场景下的作用

    目录 一.从Spring Security OAuth2官方文档了解@EnableOAuth2Sso作用 二.源码分析@EnableOAuth2Sso作用 @EnableOAuth2Client OA ...

  8. spring boot:spring security+oauth2+sso+jwt实现单点登录(spring boot 2.3.3)

    一,sso的用途 ? 1,如果有多个应用系统,用户只需要登录一次就可以访问所有相互信任的应用系统. 不需要每次输入用户名称和用户密码, 也不需要创建并记忆多套用户名称和用户密码. 2,系统管理员只需维 ...

  9. 朱晔和你聊Spring系列S1E10:强大且复杂的Spring Security(含OAuth2三角色+三模式完整例子)

    Spring Security功能多,组件抽象程度高,配置方式多样,导致了Spring Security强大且复杂的特性.Spring Security的学习成本几乎是Spring家族中最高的,Spr ...

随机推荐

  1. cocoa编程第4版 8.5 挑战1 解答

    看似简单,其实也很简单,但开始思路想错了:还上网查了一下,有网友说是将Array Controller的Keys中的personName改为personName.length,好像完全不起作用. 后来 ...

  2. "AWT-EventQueue-0" java.lang.UnsatisfiedLinkError: no freetype in java.library.path

    Exception in thread "AWT-EventQueue-0" java.lang.UnsatisfiedLinkError: no freetype in java ...

  3. 伸展树--java

    文字转载自:http://www.cnblogs.com/vamei 代码转载自:http://www.blogjava.net/javacap/archive/2007/12/19/168627.h ...

  4. .net 模糊匹配路径

    string[] fileNames = Directory.GetFiles("D:/", "*1.txt"); // 路径,模糊文件名 : 返回符合的文件名 ...

  5. Day2_元组_字典_集合_字符编码_文件处理

    元组: 作用:存多个值,元组不可变,主要用来读 age=(11,22,33,44,55) print(age[2]) #取出元组内的值 print(age[1:4]) #取出元组内的某些值 print ...

  6. (转)go rabbitmq实践

    转载自:http://www.cnblogs.com/shi-meng/p/4800080.html 1:驱动 本来打算自己写一个驱动的,后来发现github上面已经有了,那我就直接拿现成的了, 驱动 ...

  7. 【读书笔记】C++Primer---第三章

    1.由于为了与C语言兼容,字符串字面值与标准库string类型不是同一种类型: 2.以下代码中,cin有几点需要注意:a.读取并忽略开头所有的空白字符(如空格.换行符.制表符):b.读取字符直至再次遇 ...

  8. 菜鸟级Git GitHub创建仓库

    菜鸟标准:知道pwd ,rm 命令是什么. 一.Git 是什么. git 是目前世界上最先进的分布式版本控制系统 二.SVN与Git 1.版本控制系统 SVN 是集中式版本控制系统,版本库是集中放在中 ...

  9. 技术人应该学习的行话--UML统一建模语言

    新生代码农如何在硝烟弥漫的商业丛林中生存和崛起? 洞见,让一部分先遇见未来. 最近公司技术部在组织架构师培训,有幸参与.导师老刘特别推荐了UML语言的学习.回想多年来,自己习惯做一些流程图,框图或者所 ...

  10. JAVA 第一张 使用记事本编写代码

    使用记事本开发JAVA程序的步骤