Spring Security OAuth 令牌生成

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 【1月更文挑战第17天】之前写了两篇分析 Spring Security OAuth 认证流程的文章,这篇主要来分析一下,`tokenServices.createAccessToken` 方法,具体是怎么生成 Token。

之前写了两篇分析 Spring Security OAuth 认证流程的文章:

其中,最后都分析到了,经过一些列认证的逻辑代码,最终由 AbstractTokenGranter 类中的一段代码生成 Token 并返回给客户端。

image.png

上面的源代码截图中,grant 方法末尾调用了 getAccessToken 方法来获取令牌,而在这个方法当中,只有一行代码创建令牌并返回。

return tokenServices.createAccessToken(getOAuth2Authentication(client, tokenRequest));

其中,getOAuth2Authentication 方法被每一种 OAuth 授权模式对应的 Granter 各自重写了,这部分我们在之前分析过了。这篇主要来分析一下,tokenServices.createAccessToken 方法,具体是怎么生成 Token 的。

-

这里的 tokenServices 的类型是 AuthorizationServerTokenServices,这是一个接口,它只有一个实现类,就是 DefaultTokenServices,我们找到其中的 createAccessToken 方法进行分析。源码如下:

@Transactional
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
   
   

   OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
   OAuth2RefreshToken refreshToken = null;
   if (existingAccessToken != null) {
   
   
      if (existingAccessToken.isExpired()) {
   
   
         if (existingAccessToken.getRefreshToken() != null) {
   
   
            refreshToken = existingAccessToken.getRefreshToken();
            // The token store could remove the refresh token when the
            // access token is removed, but we want to
            // be sure...
            tokenStore.removeRefreshToken(refreshToken);
         }
         tokenStore.removeAccessToken(existingAccessToken);
      }
      else {
   
   
         // Re-store the access token in case the authentication has changed
         tokenStore.storeAccessToken(existingAccessToken, authentication);
         return existingAccessToken;
      }
   }

   // Only create a new refresh token if there wasn't an existing one
   // associated with an expired access token.
   // Clients might be holding existing refresh tokens, so we re-use it in
   // the case that the old access token
   // expired.
   if (refreshToken == null) {
   
   
      refreshToken = createRefreshToken(authentication);
   }
   // But the refresh token itself might need to be re-issued if it has
   // expired.
   else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
   
   
      ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken;
      if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
   
   
         refreshToken = createRefreshToken(authentication);
      }
   }

   OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
   tokenStore.storeAccessToken(accessToken, authentication);
   // In case it was modified
   refreshToken = accessToken.getRefreshToken();
   if (refreshToken != null) {
   
   
      tokenStore.storeRefreshToken(refreshToken, authentication);
   }
   return accessToken;

}

这段代码做了这么几件事:

  1. 首先,拿着授权信息 authenticationtokenStore 获取已经存在的令牌。如果你不知道 tokenStore 是什么,这里简单地介绍一下:tokenStore 是保存和查找 Token 的组件,可以有不同的实现,比如 JDBC、Redis、JWT 等,简而言之,就是通过不同的方式保存 Token,本文暂不详细讨论。
  2. 第二步,判断当前的令牌是否为空、是否过期等。如果存在且未过期,则直接返回已存在的令牌。
  3. 之后,调用 createRefreshToken 生成新的 RefreshToken,调用 createAccessToken 生成新的 AccessToken。
  4. 在返回生成的 Token 之前,还有一个步骤,就是将 RefreshToken 用 OAuth2AccessToken 中的 RefreshToken 替换掉,并再次保存。这一步骤的作用,我们之后再说。
  5. 最后返回一个 OAuth2AccessToken 类型的令牌。

-

通过查看 createRefreshTokencreateAccessToken 方法的源码,可以很简单地看出,默认情况下,RefreshToken 和 AccessToken 都是一个 UUID,但是 createAccessToken 方法中还有一个关键的步骤。我们看源码:

private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
   
   
   DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
   int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
   if (validitySeconds > 0) {
   
   
      token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
   }
   token.setRefreshToken(refreshToken);
   token.setScope(authentication.getOAuth2Request().getScope());

   return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
}

其中最后一行代码,在 accessTokenEnhancer 不为空的情况下,会调用 accessTokenEnhancer.enhance(token, authentication)accessTokenEnhancer 是 Token 增强器,enhance 方法,可以在 Token 生成之后,对已生成的 Token 进行增强,这里的增强逻辑是可以自定义的,本文暂不讨论。

值得一提的是,这里的增强逻辑也会影响到 RefreshToken,因此,在之前的 createAccessToken 代码最后,会从生成的 AccessToken 中获取 RefreshToken 覆盖之前的 UUID。

至此,就分析完了完整的令牌生成逻辑,这里最后得到的令牌,就是最终返回给客户端的令牌。

目录
相关文章
|
19天前
|
安全 Java 数据安全/隐私保护
|
23天前
|
安全 Java API
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)
42 0
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
|
23天前
|
存储 安全 前端开发
第6章 Spring Security 的 Web 安全性(2024 最新版)(上)
第6章 Spring Security 的 Web 安全性(2024 最新版)
55 0
|
23天前
|
安全 Java Go
第6章 Spring Security 的 Web 安全性(2024 最新版)(下)
第6章 Spring Security 的 Web 安全性(2024 最新版)
58 1
|
23天前
|
存储 安全 Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(下)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
24 2
|
23天前
|
安全 Cloud Native Java
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)(上)
第10章 Spring Security 的未来趋势与高级话题(2024 最新版)
29 2
|
23天前
|
存储 安全 Java
第9章 Spring Security 的测试与维护 (2024 最新版)(下)
第9章 Spring Security 的测试与维护 (2024 最新版)
21 1
|
23天前
|
安全 Java 测试技术
第9章 Spring Security 的测试与维护 (2024 最新版)(上)
第9章 Spring Security 的测试与维护 (2024 最新版)
24 0
|
23天前
|
缓存 Java 数据库
第8章 Spring Security 的常见问题与解决方案(2024 最新版)(下)
第8章 Spring Security 的常见问题与解决方案(2024 最新版)
30 0
|
23天前
|
安全 Java 数据安全/隐私保护
第8章 Spring Security 的常见问题与解决方案(2024 最新版)(上)
第8章 Spring Security 的常见问题与解决方案(2024 最新版)
34 0