使用JWT的OAuth2的SSO分析

参考:https://github.com/spring-guides/tut-spring-security-and-angular-js/blob/master/oauth2/README.adoc 
http://jwt.io/introduction/ 
本文在<使用OAuth2的SSO分析>文章的基础上扩展,使用jwt可减少了向认证服务器的请求,但jwt比swt(Simple Web Tokens)要长不少,还要依赖公钥解密. 

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

这里与<使用OAuth2的SSO分析>主要不同的是,accessToken是jwt,经过解码,转化就可成为认证授权信息,无需再向授权服务器协助获得认证授权信息,关于jwt可参看前面提供的链接.本文还修改了自定义登录页和授权页,这种方案开始接近于生产了.

一.先创建OAuth2授权服务器 
1.因为使用了自定义页面,添加了wro4j-maven-plugin插件和以下依赖到pom.xml

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

2.主类修改比较大,主类继承WebMvcConfigurerAdapter主要是注册视图控制器;
继承WebSecurityConfigurerAdapter的内部类主要修改自定义权限控制;
关键是继承AuthorizationServerConfigurerAdapter的授权服务器配置,里面配置了JwtAccessTokenConverter(密钥就在这里使用),并使用这个Bean;
@EnableResourceServer一样是放在主类上.

3.application配置将oauth的配置移到了OAuth2AuthorizationConfig内部类内部.增加了一个密钥库文件和两个freemarker页面 
启动授权服务器后,可测试了: 
a.打开浏览器输入地址http://localhost:9999/uaa/oauth/authorize?response_type=code&client_id=acme&redirect_uri=http://example.com发出请求,然后根据以上配置,输入用户名/密码,点同意,获取返回的授权许可码 
b.在Linux的bash或mac的terminal输入

[[email protected] ~]#curl acme:[email protected]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": "eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0NTk1NTUxNTYsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIiwiUk9MRV9VU0VSIl0sImp0aSI6IjI5MjcyYWJiLTQ4MjUtNGYwMS1hZjllLTg5ZGE1ZDE1MDBiNyIsImNsaWVudF9pZCI6ImFjbWUiLCJzY29wZSI6WyJvcGVuaWQiXX0.cQd88GYItHUDJuwkd_Rd0Yo8QM1R0dccuK0-xZ4OynC7EnqClLunaNOZ9jXwtilIFJNxbkbhQ8ymXdvlAF5Zjo8lpRGotdVo9rgQc39BDse7hGy1EfA9ZADQmJ-EuwkTNo0IBEXYC33XxQNK_3I_E92cnIPXq-FZHuZMRzpr-SlriwLa3aZVidmeyXK2U5dsjViWoHHKhcg-9c-VBPtyTJfPZOvj3s7DrbfCgOAGOhHkd_MBCdLDFb7QFhzIRsMfcD9rOAGTqk-hU2pHkkakKQ7_vL604UU7Qh3Zzkn6VbHPy0HAAiB9cnUhkQxK3Qb-wbHG-l3FC2pDlhtlhMHNfg",
  "token_type": "bearer",
  "refresh_token": "eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsib3BlbmlkIl0sImF0aSI6IjI5MjcyYWJiLTQ4MjUtNGYwMS1hZjllLTg5ZGE1ZDE1MDBiNyIsImV4cCI6MTQ2MjEwMzk1NiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiIzNWM5OWY0Yy0xMGM0LTQ5ZTAtODAwYi1lZTc5ZTQ3ODNkNmUiLCJjbGllbnRfaWQiOiJhY21lIn0.bUvJ9HmrFU92euLzd5eesJKFlav5v1WyfBEgd3pO6I2D2yYy98oPwfNwCrbP44M2ilO48LJEovLLoZFYvjfA8xe6XO1Fx55Tik5SrWfizAEsNFsFg25zE92T3YNocStxuJWFSVBLlwjtxpVmnHOgPefku2G6N5seziX0SOBJleHSUObNAYtiBVQjKWXA3jGnMoZSP0dMbgtrWinwRJLwvaMgMDNnxYFSdvSW99XKjCyQNVmbGa4aRyy-xblTr7qlSqdcZIdRBfKkHM5S9jaenNVc85vGAYQFPrdkRWhk4v-8nlHJiYdBa6ZspgbVWw_oPLgP8cbuzJev86q55p1gAw",
  "expires_in": 43199,
  "scope": "openid",
  "jti": "29272abb-4825-4f01-af9e-89da5d1500b7"
}

从返回结果复制access_token,继续:

[[email protected] ~]# TOKEN=eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0NTk1NTUxNTYsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIiwiUk9MRV9VU0VSSI6IjI5MjcyYWJiLTQ4MjUtNGYwMS1hZjllLTg5ZGE1ZDE1MDBiNyIsImNsaWVudF9pZCI6ImFjbWUiLCJzY29wZSI6WyJvcGVuaWQiXX0.cQd88GYItHUDJuwkd_Rd0Yo8QM1R0dccuK0-xZ4OynC7EnqClLunaNOZ9jXwtilIFJNxbkbhQ8ymXdvlAF5Zjo8lpRGotdVo9rgQc39BDse7hGy1EfA9ZADQmJ-EuwkTNo0IBEXYC33XxQNK_3I_E92cnIPXq-FZHuZMRzpr-SlriwLa3aZVidmeyXK2U5dsjViWoHHKhcg-9c-VBPtyTJfPZOvj3s7DrbfCgOAGOhHkd_MBCdLDFb7QFhzIRsMfcD9rOAGTqk-hU2pHkkakKQ7_vL604UU7Qh3Zzkn6VbHPy0HAAiB9cnUhkQxK3Qb-wbHG-l3FC2pDlhtlhMHNfg
[[email protected] ~]# curl -H “Authorization: Bearer $TOKEN” 192.168.1.115:9999/uaa/user 

第二个命令返回结果类似如下:

{
  "details": {
    "remoteAddress": "192.168.1.194",
    "sessionId": null,
    "tokenValue": "eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjE0NTk1NTUxNTYsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIiwiUk9MRV9VU0VSIl0sImp0aSI6IjI5MjcyYWJiLTQ4MjUtNGYwMS1hZjllLTg5ZGE1ZDE1MDBiNyIsImNsaWVudF9pZCI6ImFjbWUiLCJzY29wZSI6WyJvcGVuaWQiXX0.cQd88GYItHUDJuwkd_Rd0Yo8QM1R0dccuK0-xZ4OynC7EnqClLunaNOZ9jXwtilIFJNxbkbhQ8ymXdvlAF5Zjo8lpRGotdVo9rgQc39BDse7hGy1EfA9ZADQmJ-EuwkTNo0IBEXYC33XxQNK_3I_E92cnIPXq-FZHuZMRzpr-SlriwLa3aZVidmeyXK2U5dsjViWoHHKhcg-9c-VBPtyTJfPZOvj3s7DrbfCgOAGOhHkd_MBCdLDFb7QFhzIRsMfcD9rOAGTqk-hU2pHkkakKQ7_vL604UU7Qh3Zzkn6VbHPy0HAAiB9cnUhkQxK3Qb-wbHG-l3FC2pDlhtlhMHNfg",
    "tokenType": "Bearer",
    "decodedDetails": null
  },
  "authorities": [
    {
      "authority": "ROLE_ADMIN"
    },
    {
      "authority": "ROLE_USER"
    }
  ],
  "authenticated": true,
  "userAuthentication": {
    "details": null,
    "authorities": [
      {
        "authority": "ROLE_ADMIN"
      },
      {
        "authority": "ROLE_USER"
      }
    ],
    "authenticated": true,
    "principal": "user",
    "credentials": "N/A",
    "name": "user"
  },
  "credentials": "",
  "principal": "user",
  "oauth2Request": {
    "clientId": "acme",
    "scope": [
      "openid"
    ],
    "requestParameters": {
      "client_id": "acme"
    },
    "resourceIds": [],
    "authorities": [],
    "approved": true,
    "refresh": false,
    "redirectUri": null,
    "responseTypes": [],
    "extensions": {},
    "grantType": null,
    "refreshTokenRequest": null
  },
  "clientOnly": false,
  "name": "user"
}

从结果来看,使用access token访问资源一切正常,说明授权服务器没问题.

二.再看分离的资源服务器 
spring-security-jwt依赖也要加入pom.xml;
主类没改动;
application配置文件使用security.oauth2.resource.jwt.keyValue替换security.oauth2.resource.userInfoUri选项,使用这个公钥来解密jwt.

最后运行主类的main方法测试(授权服务器前面启动了,access_token也得到了),于是在使用curl命令:

[[email protected] ~]# curl -H “Authorization: Bearer $TOKEN” 192.168.1.115:9000 

返回结果类似如下:

{
  "id": "03af8be3-2fc3-4d75-acf7-c484d9cf32b1",
  "content": "Hello World"
} 

跟踪下获取认证授权的信息过程:

当使用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是DefaultTokenServices
}
org.springframework.security.oauth2.provider.token.DefaultTokenServices
#loadAuthentication{
OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);//tokenStore是JwtTokenStore
OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
}
org.springframework.security.oauth2.provider.token.store.JwtTokenStore
#readAccessToken{
OAuth2AccessToken accessToken = convertAccessToken(tokenValue);
}
org.springframework.security.oauth2.provider.token.store.JwtTokenStore
#convertAccessToken{
return jwtTokenEnhancer.extractAccessToken(tokenValue, jwtTokenEnhancer.decode(tokenValue));
}
org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter
#extractAccessToken 

经过上面这个过程,用到jwt的公钥对jwt进行解码,从中抽取OAuth2Authentication,这个Authentication本身就包含了用户认证的信息.
无需再向授权服务器发请求解码

三.UI服务器作为SSO的客户端. 
同样UI服务器也要添加spring-security-jwt依赖到pom.xml;
主类也基本不改动;
和资源服务器一样,使用security.oauth2.resource.jwt.keyValue替换security.oauth2.resource.userInfoUri选项.
其它的分析与<使用OAuth2的SSO分析>类似.
可以三台服务器都启动测试了.
http://blog.csdn.net/xiejx618/article/details/51039683

时间: 2024-08-01 22:48:13

使用JWT的OAuth2的SSO分析的相关文章

使用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服务器将/re

Spring Security 解析(六) —— 基于JWT的单点登陆(SSO)开发及原理解析

Spring Security 解析(六) -- 基于JWT的单点登陆(SSO)开发及原理解析 ??在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring Security .Spring Security Oauth2 等权限.认证相关的内容.原理及设计学习并整理一遍.本系列文章就是在学习的过程中加强印象和理解所撰写的,如有侵权请告知. 项目环境: JDK1.8 Spring boot 2.x Spring Security 5.x ?

OAuth2.0学习(4-1)Spring Security OAuth2.0 - 代码分析

1.org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter              org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter              org.springframework.security.oauth2.client.fil

spring oauth2相关资料

理解OAuth 2.0  *****http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html Secure REST API with oauth2 (翻译) *****http://blog.csdn.net/haiyan_qi/article/details/52384734 https://spring.io/guides/tutorials/spring-boot-oauth2/The OAuth 2.0 Authorization Fr

Django-jwt token生成源码分析

一. 认证的发展历程简介 这里真的很简单的提一下认证的发展历程.以前大都是采用cookie.session的形式来进行客户端的认证,带来的结果就是在数据库上大量存储session导致数据库压力增大,大致流程如下: 在该场景下,分布式.集群.缓存数据库应运而生,认证的过程大致如下: 不过该方式还是缓解不了数据库压力,一个项目中应该尽可能多的减少IO操作,于是后来采用签名的方式,在服务端只保存token的签名算法,当客户端认证时,只需用算法去生成或是判断token的合法性即可.大致方式如下: 二.

Spring Security + JWT学习

开胃:Oauth2认证流程分析 现在第三方登录已经很普遍了,随便哪个App都会有使用微信登录,使用手机号码登录,或者使用支付宝登录等功能... 下面我们就以使用微信登录,做一个简单的流程分析分析 开胃:JWT认识 在上面的Oauth2的认证流程中,我们就可以看出一些猫腻来: 在我们拿着令牌去用户信息系统调用用户的相关信息的时候, 用户信息系统其实去请求了授权服务器,验证l了该令牌的合法性 这样每次认证都需要去调用授权服务器做一个令牌合法性验证,显得效率低下. JWT令牌思想 JWT令牌场景运用

Securing Spring Cloud Microservices With OAuth2

From Zero to OAuth2 in Spring cloud Today I am presenting hours of research about a (apparently) simple question: “How can I maintain security in my microservices architecture?”. The task is to enable a simple but mighty possibility to secure spring

OAuth 2和JWT - 如何设计安全的API?

OAuth 2和JWT - 如何设计安全的API? Moakap译,原文 OAuth 2 VS JSON Web Tokens: How to secure an API 本文会详细描述两种通用的保证API安全性的方法:OAuth2和JSON Web Token (JWT) 假设: 你已经或者正在实现API: 你正在考虑选择一个合适的方法保证API的安全性: JWT和OAuth2比较? 要比较JWT和OAuth2?首先要明白一点就是,这两个根本没有可比性,是两个完全不同的东西. JWT是一种认证

JWT简单讲解

一.什么是JWT JWT(JSON Web Token) 是一个开放标准(RFC 7519),它定义了一种紧凑的.自包含的方式,用于作为JSON对象在各方之间安全地传输信息.该信息可以被验证和信任,因为它是数字签名的. 二.使用场景(来自理解JWT的使用场景和优劣) 一次性验证: 比如用户注册后需要发一封邮件让其激活账户,通常邮件中需要有一个链接,这个链接需要具备以下的特性:能够标识用户,该链接具有时效性(通常只允许几小时之内激活),不能被篡改以激活其他可能的账户…这种场景就和 jwt 的特性非