授权系统源码_spring security oauth2源码解析

d7d6d7a8b59844ebf604d7cf0e829dc3.gif

当你掌握某个技术,每次再重新去研究的时候都会有不一样的感悟。随的时间的推移每个人的能力都有不一样的提高,这时候再去回头看就会有更清晰的感觉。本文主要就是对spring security oauth2的源码进行解读,希望能帮助到大家同时自已也做一下学习记录。https://projects.spring.io/spring-security-oauth/docs/oauth2.html要对源码进行分析先大概了解一下官方文档说明。如图官方某段描述:

627e4d27d8737123670c7efcefc74007.png

AuthorizationEndpoint:用于服务授权请求。 请求地址:/oauth/authorize

TokenEndPoint: 用于服务访问令牌的请求。请求地址:/oauth/token

OAuth2AuthenticationProcessingFilter:用于加载给定的认证访问令牌请求的认证

生成token的流程图:

dc88c1e6e921c024696955a3e1c0f6b4.png

@EnableAuthorizationServer源码:授权认证服务器核心注解

8ebd5b38a81b92f509885b51b1cb9bda.png

从源码我们知道引入了AuthorizationServerEndpointsConfiguration和AuthorizationServerSecurityConfiguration这2个配置类。

AuthorizationServerEndpointsConfiguration源码

82c676137564c5d9fb563a8af8357c05.png

   从官方文档我们知道这个配置类会存在AuthorizationEndpoint和TokenEndPoint创建。

AuthorizationServerSecurityConfiguration源码如下:

c4d8037f9e2a2e8da6c54f18aab53b50.png

1eadcf5effa23db099ea48de1a34dc11.png

主要的配置ClientDetailsService、UserDetailsService

ClientDetailsService

64219dbebac3850e8eb0e2f8be80df57.png

内部仅有loadClientByClientId(),从方法名我们可以知道是通过clientId来获取client信息,官方提供俩个实现类,我们也可以像UserDetailsService一样自已编写实现类。
UserDetailsService

4ed91f396de31805ac1426f47b6a1333.png

UserDetailsService

669347c88d6a7b099fe4284f703183c7.png内部仅有loadUserByUsername方法

ClientDetailsUserDetailsService

6f913782adca557c23286fa08d204335.png

UserDetailsService子类,内部维护了ClientDetailsService, loadUserByUsername()方法重写后调用了ClientDetailsService的loadClientByClientId()方法。
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {  //.配置AuthorizationServer安全认证的相关信息,创建ClientCredentialsTokenEndpointFilter  @Override  public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {  }   //配置oauth2的客户端相关信息  @Override  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {  }  //配置ClientCredentialsTokenEndpointFilter相关类  //配置身份认证器、配置认证方式  //TokenStore,TokenGranter,OAuth2RequestFactory  @Override  public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {  }}

clientCredentialsTokenEndPointFilter源码

e7cb0742b2931ab0633a30c2615dc1a3.png

1.拦截/oauth/token,获取到clientId和clientsecret信息

2.创建UsernamepasswordAutherticationToken

3.作为AuthenticationManager().authenticate(authRequest)参数调用认证过程

整个认证过程唯一最大的区别在于:

DaoAuthenticationProvider.retrieveUser() 获取认证用户信息时调用的是 ClientDetailsUserDetailsService,根据前面讲述的其内部其实是调用ClientDetailsService 获取到客户端信息。

ba6bd33f7bea897362ca674fc7acc062.png

@EnableResourceServer解析

8b0ac456073adc2390f2b5ca4d612a42.png

 从源码我们知道引入了ResourceServerConfiguration配置类,这个配置类是应用了ResourceServerSecurityConfigurer

ce78cbfd7056a13aae0d2c4b4797731d.png

ResourceServerSecurityConfigurer的源码

30891d9a007c7725e0acef9575d0302f.png

      1.创建OAuth2AuthenticationManager对象      2.创建OAuth2AuthenticationProcessingFilter 过滤器      3.将OAuth2AuthenticationProcessingFilter过滤器加载到过滤链上 AuthorizationEndPoint源码

2248232b7730357efd8c795362e4ad83.png

1.getOAuth2RequestFactory根据参数创建AuthorizationRequest。2.判断principal是否已授权,oauth/authorize 设置为无权限访问 ,如果 判断失败则抛出 InsufficientAuthenticationException异常会被 ExceptionTranslationFilter 处理 ,然后重定向到登录页。3.通过getClientDetailsService()获取到ClientDetails客户端信息。4.获取参数中的回调地址和系统配置的回调地址对比。5.验证scope。6.检测客户端是否设置自动授权,客户端配置autoApprove(true)。7.如果设置 autoApprove(true) 则 调用 getAuthorizationCodeResponse() 方法生成code码并回调到设置的回调地址。8.真实生成Code 的方法是generateCode(AuthorizationRequest authorizationRequest, Authentication authentication) 方法:其内部是调用authorizationCodeServices.createAuthorizationCode()方法生成code。 TokenEndPoint解析

6d7bd71d3059f63ab12a689538989237.png

1.从principal中获取clientId,进而load client信息。2.从paramters中拿clientid,scope,grantType组装tokenRequest。3.校验client信息,确保从principal拿到的client信息与根据paramters得到的client信息一致。4.根据grantType设置tokenRequest的scope,授权类型有password 模式、authorization_code 模式、refresh_token 模式、client_credentials 模式、implicit 模式。5.如果是授权模式,则清空scope,因为授权请求会确定scope,所以没必要传。6.如果是刷新Token模式,解析并设置scope。7.通过令牌授予者获取token。
TokenGranter

52f46159ec549a015177fb27b2424d67.png

CompositeTokenGranter源码

07d7585229c63cf65f2ca9d64db4125e.png

官方默认调用CompositeTokenGranter的grant()方法,debug追踪知道默认有五个子类加上一个共同的父类(AbstractTokenGranter),然后遍历尝试看使用的是哪种授权方式,ClientCredentialsTokenGranter重写了父类的grant()方法,其余四种都是直接调用父类进行处理。

AbstractTokenGranter.grant()方法实现

47ae12ec6fc2ae9630816397e5048a6a.png

1.判断grantType是否匹配。2.获取client信息并验证grantType。3.调用用 getAccess T oken()方法生成token返回。 getAccessToken()方法实现

ad97b5345e13321d39131d84c146ab83.png

1.通过getOAuth2Authentication()方法获取OAuth2Authentication对象。

       2.调用tokenServices.createAccessToken()方法生成token      AuthorizationCodeTokenGranter.getOAuth2Authentication()方法实现

ff501216b6b1c88fb033cfc47bdf38c9.png

1.从tokenRequest中获取code和回调url

2.authorizationCodeServices.consumeAuthorizationCode(authorizationCode)通过code获取OAuth2Authentication对象

3.从OAuth2Authentication对象获取OAuth2Request对象并验证回调url和clientid

4.创建一个新的OAuth2Request,并从OAuth2Authentication中获取到Authentication对象

5.通过新的OAuth2Request对象和Authentication对象创建一个全新的OAuth2Authentication对象

生成OAuth2Authentication调用tokenServices.createAccessToken()   

cde14ce3f5555a68d49d9a475f956998.png查看DefaultTokenServices(AuthorizationServerTokenServices的实现类)的createAccessToken()方法

c07e8352b70366296ef29edf791e81b3.png

1.通过tokenStore获取之前存在的token并判断是否为空和是否过期,如果不为空且未过期就直接返回token(我们常用Jwt 这里是 JwtTokenStore ,且 existingAccessToken 永远为空,即每次请求获取token的值均不同,跟RedisTokenStore 是不一样的)。

2.调用createRefreshToken()生成refreshToken。

  3.调用createAccessToken(authentication, refreshToken)生成accesstoken。

091633172023f26aa38f4b587ebab1e3.png

从源码看出token通过uuid生成的,生成过程相对简单,但是如果我们配置了token增强器(TokenEnhancer)jwtToken就是使用了增强器实现。

       4.重新覆盖原有的刷新token(原有的 refreshToken 为UUID 数据,覆盖为 jwtToken)并返回token。

        token的源码解析流程图如下:

338824591d9c5a5039b3b90c71928198.png

OAuth2AuthenticationProcessingFilter解析

 我们获取到了token,就会通过token去拿到资源信息。那么资源服务器是如何通过传入的token去辨别用户并允许返回资源信息的。

979e951e95ba2f09581cc3d8226b69b7.png

226ad01371c7f62f1919da5d83a13bf4.png

整个oauth2与HttpSecurity相关的核心配置

1.创建OAuth2AuthenticationProcessingFilter

2.为OAuth2AuthenticationProcessingFilter提供固定的AuthenticationManager即OAuth2AuthenticationManager,它并没有将OAuth2AuthenticationManager添加到spring的容器中,不然可能会影响spring security的普通认证流程(非oauth2请求),只有被OAuth2AuthenticationProcessingFilter拦截到的oauth2相关请求才被特殊的身份认证器处理。 

3.设置了TokenExtractor默认的实现—-BearerTokenExtractor

4.相关的异常处理器,可以重写实现

0062ebe6e1bfd996d3ee45735935f556.png

1.调用tokenExtractor.extract()从请求中解析出token信息并存放到authentication 的 principal 字段 中

2.调用 authenticationManager.authenticate() 认证过程:注意此时的 authenticationManager 是 OAuth2AuthenticationManager

 authenticationManager.authenticate() 方法实现

000f3a6107f7da861199b09b77efb05b.png

1.从authentication获取token

2.调用tokenServices.loadAuthentication()方法通过token参数获取到OAuth2Authentication对象,tokenServices就是我们资源服务器所配置的。

3.checkClientDetails()检测客户端信息,由于授权服务器和资源服务器分离设计,这个检测方法实际没有进行检测

4.设置认证成功标识并返回,返回的是OAuth2Authentication

回顾:

创建token需要的几个必要类 :

clientDetailsService,authorizationServerTokenServices,ClientDetails ,TokenRequest,OAuth2Request,authentication和OAuth2Authentication 。要了解这几个类直接的联系。

clientDetailsService和authorizationServerTokenServices可以直接从spring 容器获取,ClientDetails可以从请求参数中获取,有了ClientDetails 就有了TokenRequest,有了TokenRequest和authentication就有了OAuth2Authentication,有了OAuth2Authentication就能生成OAuth2AccessToken。

对spring security oauth2的源码解析完毕,有觉得还不错的小伙伴可以关注我,更多精彩好文等得你。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值