之前写了两篇分析 Spring Security OAuth 认证流程的文章:
其中,最后都分析到了,经过一些列认证的逻辑代码,最终由 AbstractTokenGranter
类中的一段代码生成 Token 并返回给客户端。
上面的源代码截图中,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;
}
这段代码做了这么几件事:
- 首先,拿着授权信息
authentication
从tokenStore
获取已经存在的令牌。如果你不知道tokenStore
是什么,这里简单地介绍一下:tokenStore
是保存和查找 Token 的组件,可以有不同的实现,比如 JDBC、Redis、JWT 等,简而言之,就是通过不同的方式保存 Token,本文暂不详细讨论。 - 第二步,判断当前的令牌是否为空、是否过期等。如果存在且未过期,则直接返回已存在的令牌。
- 之后,调用
createRefreshToken
生成新的 RefreshToken,调用createAccessToken
生成新的 AccessToken。 - 在返回生成的 Token 之前,还有一个步骤,就是将 RefreshToken 用 OAuth2AccessToken 中的 RefreshToken 替换掉,并再次保存。这一步骤的作用,我们之后再说。
- 最后返回一个 OAuth2AccessToken 类型的令牌。
-
通过查看 createRefreshToken
和 createAccessToken
方法的源码,可以很简单地看出,默认情况下,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。
至此,就分析完了完整的令牌生成逻辑,这里最后得到的令牌,就是最终返回给客户端的令牌。