Spring Security Oauth2 认证(获取token/刷新token)流程(password模式)

1.本文介绍的认证流程范围

本文主要对从用户发起获取token的请求(/oauth/token),到请求结束返回token中间经过的几个关键点进行说明。

2.认证会用到的相关请求

注:所有请求均为post请求。

  • 获取access_token请求(/oauth/token) 
    请求所需参数:client_id、client_secret、grant_type、username、password
<span style="color:#000000"><code><span style="color:#000088">http</span>://localhost/oauth/<span style="color:#000088">token</span>?client_id=demoClientId&client_secret=demoClientSecret&grant_type=password&username=demoUser&password=<span style="color:#006666">50575</span>tyL86xp29O380t1</code></span>
  • 1
  • 检查头肯是否有效请求(/oauth/check_token) 
    请求所需参数:token
<span style="color:#000000"><code><span style="color:#000088">http</span>://localhost/oauth/check_token?<span style="color:#000088">token</span>=f57ce129-<span style="color:#006666">2</span>d4d-<span style="color:#006666">4</span>bd7-<span style="color:#006666">1111</span>-f31ccc69d4d1</code></span>
  • 1
  • 刷新token请求(/oauth/token) 
    请求所需参数:grant_type、refresh_token、client_id、client_secret 
    其中grant_type为固定值:grant_type=refresh_token
<span style="color:#000000"><code>http://localhost/oauth/token?grant_<span style="color:#4f4f4f">type</span>=refresh_token&refresh_token=fbde81ee-f419-<span style="color:#006666">42</span>b1-<span style="color:#006666">1234</span>-<span style="color:#006666">9191</span>f1f95be9&client_id=demoClientId&client_secret=demoClientSecret</code></span>
  • 1

2.认证核心流程

注:文中介绍的认证服务器端token存储在Reids,用户信息存储使用数据库,文中会包含相关的部分代码。

2.1.获取token的主要流程:

加粗内容为每一步的重点,不想细看的可以只看加粗内容:

  1. 用户发起获取token的请求。
  2. 过滤器会验证path是否是认证的请求/oauth/token,如果为false,则直接返回没有后续操作。
  3. 过滤器通过clientId查询生成一个Authentication对象
  4. 然后会通过username和生成的Authentication对象生成一个UserDetails对象,并检查用户是否存在。
  5. 以上全部通过会进入地址/oauth/token,即TokenEndpoint的postAccessToken方法中。
  6. postAccessToken方法中会验证Scope,然后验证是否是refreshToken请求等。
  7. 之后调用AbstractTokenGranter中的grant方法。
  8. grant方法中调用AbstractUserDetailsAuthenticationProvider的authenticate方法,通过username和Authentication对象来检索用户是否存在
  9. 然后通过DefaultTokenServices类从tokenStore中获取OAuth2AccessToken对象
  10. 然后将OAuth2AccessToken对象包装进响应流返回

2.2.刷新token(refresh token)的流程

刷新token(refresh token)的流程与获取token的流程只有⑨有所区别:

  • 获取token调用的是AbstractTokenGranter中的getAccessToken方法,然后调用tokenStore中的getAccessToken方法获取token。
  • 刷新token调用的是RefreshTokenGranter中的getAccessToken方法,然后使用tokenStore中的refreshAccessToken方法获取token。

2.3.tokenStore的特点

tokenStore通常情况为自定义实现,一般放置在缓存或者数据库中。此处可以利用自定义tokenStore来实现多种需求,如:

  • 同已用户每次获取token,获取到的都是同一个token,只有token失效后才会获取新token。
  • 同一用户每次获取token都生成一个完成周期的token并且保证每次生成的token都能够使用(多点登录)。
  • 同一用户每次获取token都保证只有最后一个token能够使用,之前的token都设为无效(单点token)。

3.获取token的详细流程(代码截图)

3.1.代码截图梳理流程

1.一个比较重要的过滤器 
这里写图片描述
2.此处是①中的attemptAuthentication方法 
这里写图片描述
3.此处是②中调用的authenticate方法 
这里写图片描述
4.此处是③中调用的AbstractUserDetailsAuthenticationProvider类的authenticate方法 
这里写图片描述
5.此处是④中调用的DaoAuthenticationProvider类的retrieveUser方法 
这里写图片描述
6.此处为⑤中调用的ClientDetailsUserDetailsService类的loadUserByUsername方法,执行完后接着返回执行④之后的方法 
这里写图片描述
7.此处为④中调用的DaoAuthenticationProvider类的additionalAuthenticationChecks方法,此处执行完则主要过滤器执行完毕,后续会进入/oauth/token映射的方法。 
这里写图片描述
8.此处进入/oauth/token映射的TokenEndpoint类的postAccessToken方法 
这里写图片描述
9.此处为⑧中调用的AbstractTokenGranter类的grant方法 
这里写图片描述
10.此处为⑨中调用的ResourceOwnerPasswordTokenGranter类中的getOAuth2Authentication方法 
这里写图片描述
11.此处为⑩中调用的自定义的CustomUserAuthenticationProvider类中的authenticate方法,此处校验用户密码是否正确,此处执行完则返回⑨执行后续方法。 
这里写图片描述
12.此处为⑨中调用的DefaultTokenServices中的createAccessToken方法 
这里写图片描述
13.此处为12中调用的RedisTokenStore中的getAccessToken方法等,此处执行完,则一直向上返回到⑧中执行后续方法。 
这里写图片描述
14.此处为⑧中获取到token后需要包装返回流操作 
这里写图片描述

3.2.示例中spring-security.xml的部分配置

<span style="color:#000000"><code class="language-xml"><span style="color:#880000"><!-- 认证地址 --></span>
<span style="color:#006666"><<span style="color:#4f4f4f">sec:http</span> <span style="color:#4f4f4f">pattern</span>=<span style="color:#009900">"/oauth/token"</span> <span style="color:#4f4f4f">create-session</span>=<span style="color:#009900">"stateless"</span>
              <span style="color:#4f4f4f">authentication-manager-ref</span>=<span style="color:#009900">"authenticationManager"</span> ></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">sec:intercept-url</span> <span style="color:#4f4f4f">pattern</span>=<span style="color:#009900">"/oauth/token"</span> <span style="color:#4f4f4f">access</span>=<span style="color:#009900">"IS_AUTHENTICATED_FULLY"</span> /></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">sec:anonymous</span> <span style="color:#4f4f4f">enabled</span>=<span style="color:#009900">"false"</span> /></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">sec:http-basic</span> <span style="color:#4f4f4f">entry-point-ref</span>=<span style="color:#009900">"clientAuthenticationEntryPoint"</span> /></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">sec:custom-filter</span> <span style="color:#4f4f4f">ref</span>=<span style="color:#009900">"clientCredentialsTokenEndpointFilter"</span> <span style="color:#4f4f4f">before</span>=<span style="color:#009900">"BASIC_AUTH_FILTER"</span> /></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">sec:access-denied-handler</span> <span style="color:#4f4f4f">ref</span>=<span style="color:#009900">"oauthAccessDeniedHandler"</span> /></span>
<span style="color:#006666"></<span style="color:#4f4f4f">sec:http</span>></span>

<span style="color:#006666"><<span style="color:#4f4f4f">bean</span> <span style="color:#4f4f4f">id</span>=<span style="color:#009900">"clientAuthenticationEntryPoint"</span>
          <span style="color:#4f4f4f">class</span>=<span style="color:#009900">"org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"</span>></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">property</span> <span style="color:#4f4f4f">name</span>=<span style="color:#009900">"realmName"</span> <span style="color:#4f4f4f">value</span>=<span style="color:#009900">"springsec/client"</span> /></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">property</span> <span style="color:#4f4f4f">name</span>=<span style="color:#009900">"typeName"</span> <span style="color:#4f4f4f">value</span>=<span style="color:#009900">"Basic"</span> /></span>
<span style="color:#006666"></<span style="color:#4f4f4f">bean</span>></span>

<span style="color:#006666"><<span style="color:#4f4f4f">bean</span> <span style="color:#4f4f4f">id</span>=<span style="color:#009900">"clientCredentialsTokenEndpointFilter"</span>
      <span style="color:#4f4f4f">class</span>=<span style="color:#009900">"org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter"</span>></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">property</span> <span style="color:#4f4f4f">name</span>=<span style="color:#009900">"authenticationManager"</span> <span style="color:#4f4f4f">ref</span>=<span style="color:#009900">"authenticationManager"</span> /></span>
<span style="color:#006666"></<span style="color:#4f4f4f">bean</span>></span>

<span style="color:#006666"><<span style="color:#4f4f4f">bean</span> <span style="color:#4f4f4f">id</span>=<span style="color:#009900">"oauthAccessDeniedHandler"</span>
      <span style="color:#4f4f4f">class</span>=<span style="color:#009900">"org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"</span>></span>
<span style="color:#006666"></<span style="color:#4f4f4f">bean</span>></span>


<span style="color:#880000"><!-- 认证管理器--></span>
<span style="color:#006666"><<span style="color:#4f4f4f">sec:authentication-manager</span> <span style="color:#4f4f4f">alias</span>=<span style="color:#009900">"authenticationManager"</span>></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">sec:authentication-provider</span> <span style="color:#4f4f4f">user-service-ref</span>=<span style="color:#009900">"clientDetailsUserService"</span> /></span>
<span style="color:#006666"></<span style="color:#4f4f4f">sec:authentication-manager</span>></span>

<span style="color:#880000"><!-- 注入自定义clientDetails--></span>
<span style="color:#006666"><<span style="color:#4f4f4f">bean</span> <span style="color:#4f4f4f">id</span>=<span style="color:#009900">"clientDetailsUserService"</span>
      <span style="color:#4f4f4f">class</span>=<span style="color:#009900">"org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService"</span>></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">constructor-arg</span> <span style="color:#4f4f4f">ref</span>=<span style="color:#009900">"clientDetails"</span> /></span>
<span style="color:#006666"></<span style="color:#4f4f4f">bean</span>></span>

<span style="color:#880000"><!-- 自定义clientDetails--></span>
<span style="color:#006666"><<span style="color:#4f4f4f">bean</span> <span style="color:#4f4f4f">id</span>=<span style="color:#009900">"clientDetails"</span> <span style="color:#4f4f4f">class</span>=<span style="color:#009900">"com.xxx.core.framework.oauth.CustomClientDetailsServiceImpl"</span>></span>
<span style="color:#006666"></<span style="color:#4f4f4f">bean</span>></span>
<span style="color:#880000"><!-- 注入自定义provider--></span>
<span style="color:#006666"><<span style="color:#4f4f4f">sec:authentication-manager</span> <span style="color:#4f4f4f">id</span>=<span style="color:#009900">"userAuthenticationManager"</span>></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">sec:authentication-provider</span> <span style="color:#4f4f4f">ref</span>=<span style="color:#009900">"customUserAuthenticationProvider"</span> /></span>
<span style="color:#006666"></<span style="color:#4f4f4f">sec:authentication-manager</span>></span>
<span style="color:#880000"><!--自定义用户认证provider--></span>
<span style="color:#006666"><<span style="color:#4f4f4f">bean</span> <span style="color:#4f4f4f">id</span>=<span style="color:#009900">"customUserAuthenticationProvider"</span>
      <span style="color:#4f4f4f">class</span>=<span style="color:#009900">"com.xxx.core.framework.oauth.CustomUserAuthenticationProvider"</span>></span>
<span style="color:#006666"></<span style="color:#4f4f4f">bean</span>></span>

<span style="color:#006666"><<span style="color:#4f4f4f">oauth:authorization-server
</span>        <span style="color:#4f4f4f">client-details-service-ref</span>=<span style="color:#009900">"clientDetails"</span> <span style="color:#4f4f4f">token-services-ref</span>=<span style="color:#009900">"tokenServices"</span> <span style="color:#4f4f4f">check-token-enabled</span>=<span style="color:#009900">"true"</span> ></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">oauth:authorization-code</span> /></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">oauth:implicit</span>/></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">oauth:refresh-token</span>/></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">oauth:client-credentials</span> /></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">oauth:password</span> <span style="color:#4f4f4f">authentication-manager-ref</span>=<span style="color:#009900">"userAuthenticationManager"</span>/></span>
<span style="color:#006666"></<span style="color:#4f4f4f">oauth:authorization-server</span>></span>

<span style="color:#880000"><!-- 自定义tokenStore--></span>
<span style="color:#006666"><<span style="color:#4f4f4f">bean</span> <span style="color:#4f4f4f">id</span>=<span style="color:#009900">"tokenStore"</span>
      <span style="color:#4f4f4f">class</span>=<span style="color:#009900">"com.xxx.core.framework.oauth.RedisTokenStore"</span> /></span>

<span style="color:#880000"><!-- 设置access_token有效期,设置支持refresh_token,refresh_token有效期默认为30天--></span>
<span style="color:#006666"><<span style="color:#4f4f4f">bean</span> <span style="color:#4f4f4f">id</span>=<span style="color:#009900">"tokenServices"</span>
      <span style="color:#4f4f4f">class</span>=<span style="color:#009900">"org.springframework.security.oauth2.provider.token.DefaultTokenServices"</span>></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">property</span> <span style="color:#4f4f4f">name</span>=<span style="color:#009900">"tokenStore"</span> <span style="color:#4f4f4f">ref</span>=<span style="color:#009900">"tokenStore"</span> /></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">property</span> <span style="color:#4f4f4f">name</span>=<span style="color:#009900">"supportRefreshToken"</span> <span style="color:#4f4f4f">value</span>=<span style="color:#009900">"true"</span> /></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">property</span> <span style="color:#4f4f4f">name</span>=<span style="color:#009900">"accessTokenValiditySeconds"</span> <span style="color:#4f4f4f">value</span>=<span style="color:#009900">"43200"</span>></span><span style="color:#006666"></<span style="color:#4f4f4f">property</span>></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">property</span> <span style="color:#4f4f4f">name</span>=<span style="color:#009900">"clientDetailsService"</span> <span style="color:#4f4f4f">ref</span>=<span style="color:#009900">"clientDetails"</span> /></span>
<span style="color:#006666"></<span style="color:#4f4f4f">bean</span>></span>
</code></span>

4.总结

本文中的流程能够结果的问题:

  • 需要自定义修改获取到的token
  • token单点问题
  • 使用refresh_token的情况
  • 9
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值