在《SpringSecurity OAuth2 获取Token端点TokenEndpoint、Token授权TokenGranter接口 详解》中,在TokenGranter接口的几个实现类中,真正实现token生成的其实是AuthorizationServerTokenServices对象。这一篇,我们专门来分析AuthorizationServerTokenServices是如何实现token生成的。
AuthorizationServerTokenServices接口提供了创建Token、刷新Token、获取Token的方法,如下所示:
public interface AuthorizationServerTokenServices {
OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException;
OAuth2AccessToken refreshAccessToken(String refreshToken, TokenRequest tokenRequest)
throws AuthenticationException;
OAuth2AccessToken getAccessToken(OAuth2Authentication authentication);
}
默认提供了DefaultTokenServices实现类,该实现类除了实现AuthorizationServerTokenServices 接口定义的方法,还实现了ResourceServerTokenServices、ConsumerTokenServices和InitializingBean接口。
其中,实现InitializingBean接口,主要为了校验当初始化完成后,保证tokenStore属性的值不能为空。
createAccessToken()方法实现了根据OAuth2Authentication对象(认证用户信息及其请求信息)创建OAuth2AccessToken对象(Token信息)的功能,即实现了创建accessToken的功能。下面我们通过代码注释,分析该方法的实现逻辑:
@Transactional
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
//根据authentication查询已经存在的OAuth2AccessToken对象,这里使用到了TokenStore对象,后续专门分析
OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
OAuth2RefreshToken refreshToken = null;
//当存在OAuth2AccessToken对象时,进行验证是否过期
if (existingAccessToken != null) {
//如果OAuth2AccessToken对象已经过期,就删除tokenStore中存储的refreshToken和AccessToken对象
if (existingAccessToken.isExpired()) {
if (existingAccessToken.getRefreshToken() != null) {
refreshToken = existingAccessToken.getRefreshToken();
tokenStore.removeRefreshToken(refreshToken);
}
tokenStore.removeAccessToken(existingAccessToken);
}else {//如果没有过期,说明当前accessToken可以用,就重新存储AccessToken,避免authentication已经发生变化,导致不一致,然后直接返回当前的existingAccessToken即可。
tokenStore.storeAccessToken(existingAccessToken, authentication);
return existingAccessToken;
}
}
//如果不存在过期AccessToken关联的refreshToken,就调用haicreateRefreshToken()方法创建一个新的refreshToken对象,存在的话,尝试利旧使用
if (refreshToken == null) {
refreshToken = createRefreshToken(authentication);
}
//验证是否利旧使用,如果refreshToken过期,还是需要createRefreshToken()方法创建一个新的refreshToken对象,否则可以利旧使用
else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken;
if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
refreshToken = createRefreshToken(authentication);
}
}
//使用上面已经处理过的refreshToken对象和authentication对象,通过调用createAccessToken()方法创建accessToken对象
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
//调用tokenStore的storeAccessToken()方法,存储创建好的accessToken对象
tokenStore.storeAccessToken(accessToken, authentication);
//调用tokenStore的storeRefreshToken()方法,存储上面处理过的refreshToken对象
refreshToken = accessToken.getRefreshToken();
if (refreshToken != null) {
tokenStore.storeRefreshToken(refreshToken, authentication);
}
//最后,返回创建的accessToken对象即可
return accessToken;
}
createRefreshToken()方法实现了根据OAuth2Authentication创建OAuth2RefreshToken对象的功能,实际创建的就是DefaultOAuth2RefreshToken实例对象(实际上就是一个带有value属性字段的实体类,value字段就是OAuth2RefreshToken对象的值,默认就是一个UUID的随机字符串)或DefaultExpiringOAuth2RefreshToken对象(比DefaultOAuth2RefreshToken多了一个过期时间参数)。
private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
//判断是否支持refresh_token,根据客户端信息中的grantType来进行判断,如果不支持直接返回null,说明refresh_token可能为空
if (!isSupportRefreshToken(authentication.getOAuth2Request())) {
return null;
}
//获取刷新token的有效时间,也是通过客户端信息中refreshTokenValiditySeconds字段进行自定义,系统默认为60 * 60 * 24 * 30(30天)
int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request());
//刷新token的value值,UUID随机字符串
String value = UUID.randomUUID().toString();
//当存在有效时间时,创建DefaultExpiringOAuth2RefreshToken实例对象
if (validitySeconds > 0) {
return new DefaultExpiringOAuth2RefreshToken(value, new Date(System.currentTimeMillis()
+ (validitySeconds * 1000L)));
}
//没有过期时间时,创建DefaultOAuth2RefreshToken对象
return new DefaultOAuth2RefreshToken(value);
}
createAccessToken()方法实现了根据OAuth2Authentication、OAuth2RefreshToken对象创建OAuth2AccessToken对象的功能,默认创建的是DefaultOAuth2AccessToken对象,该对象可以通过TokenEnhancer进行自定义access token 对象。
private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
//创建默认的DefaultOAuth2AccessToken对象,其中的value默认使用的还是UUID字符串
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
//和刷新token过期时间类似,首先从客户端的accessTokenValiditySeconds属性字段获取,没有的话,则使用默认值(60 * 60 * 12,12小时)
int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
//设置accessToken过期时间
if (validitySeconds > 0) {
token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
}
//设置accessToken的刷新token,有可能为空
token.setRefreshToken(refreshToken);
//设置accessToken的scope值
token.setScope(authentication.getOAuth2Request().getScope());
//如果accessTokenEnhancer 不为空,则使用accessTokenEnhancer 进行token内容自定义设置,否则,直接返回默认的token即可,关于accessTokenEnhancer如何增加自定义内容,后续再展开分析。
return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
}
refreshAccessToken()方法,主要是实现accessToken的刷新功能,这里需要使用到刷新token的value值和TokenRequest对象。下面我们通过代码注释,分析该方法的实现逻辑:
@Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
throws AuthenticationException {
//如果不支持刷新token,直接抛出InvalidGrantException异常
if (!supportRefreshToken) {
throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
}
//查询刷新token对象,如果为空,直接抛出InvalidGrantException异常,关于tokenStore根据refreshTokenValue获取OAuth2RefreshToken 对象的方法,后续再展开分析
OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
if (refreshToken == null) {
throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
}
//根据刷新token对象,获取对应的OAuth2Authentication 对象,用来校验用户认证信息
OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
//当authenticationManager 不为空时,重写认证用户信息,并生成新的OAuth2Authentication对象
if (this.authenticationManager != null && !authentication.isClientOnly()) {
Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities());
user = authenticationManager.authenticate(user);
Object details = authentication.getDetails();
authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
authentication.setDetails(details);
}
//校验clientId和请求中的clientId是否一致
String clientId = authentication.getOAuth2Request().getClientId();
if (clientId == null || !clientId.equals(tokenRequest.getClientId())) {
throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);
}
//删除当前刷新token关联的accessToken对象,避免存在多个accessToken
tokenStore.removeAccessTokenUsingRefreshToken(refreshToken);
//如果刷新token已经过期了,这移除该token,并抛出InvalidTokenException异常
if (isExpired(refreshToken)) {
tokenStore.removeRefreshToken(refreshToken);
throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken);
}
//创建一个刷新使用的OAuth2Authentication对象
authentication = createRefreshedAuthentication(authentication, tokenRequest);
//如果refreshToken 不能被重复利用,就移除tokenStore中存储的refreshToken 对象,并重写创建一个新的refreshToken 对象,使用到的createRefreshToken()方法用来创建刷新token,前面已经分析过了,这里不再重复。
if (!reuseRefreshToken) {
tokenStore.removeRefreshToken(refreshToken);
refreshToken = createRefreshToken(authentication);
}
//创建一个新的OAuth2AccessToken对象,即刷新了原来的OAuth2AccessToken对象,并把该对象存储到tokenStore中
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
//如果refreshToken 不能被重复利用,说明该对象已经被重写创建了,这里需要把该对象再存储到tokenStore中
if (!reuseRefreshToken) {
tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
}
return accessToken;
}
createRefreshedAuthentication()方法主要用来创建OAuth2Authentication对象,主要刷新了OAuth2Request对象,改变了该对象的scope值,具体实现如下:
private OAuth2Authentication createRefreshedAuthentication(OAuth2Authentication authentication, TokenRequest request) {
OAuth2Authentication narrowed = authentication;
Set<String> scope = request.getScope();
OAuth2Request clientAuth = authentication.getOAuth2Request().refresh(request);
if (scope != null && !scope.isEmpty()) {
Set<String> originalScope = clientAuth.getScope();
if (originalScope == null || !originalScope.containsAll(scope)) {
throw new InvalidScopeException("Unable to narrow the scope of the client authentication to " + scope
+ ".", originalScope);
}
else {
clientAuth = clientAuth.narrowScope(scope);
}
}
narrowed = new OAuth2Authentication(clientAuth, authentication.getUserAuthentication());
return narrowed;
}
getAccessToken()方法,主要提供了查询accessToken的功能,实际就是通过tokenStore对象,根据OAuth2Authentication参数查询OAuth2AccessToken对象,实现如下:
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
return tokenStore.getAccessToken(authentication);
}
在AuthorizationServerTokenServices 中,无论是createAccessToken()、refreshAccessToken()方法还是getAccessToken()方法,都是基于TokenStore对象实现token的存储和查询功能的,这里我们专门学习一下TokenStore的具体实现。
TokenStore 定义了Token管理需要的一些方法,具体如下所示:
public interface TokenStore {
// 根据AccessToken对象查询对应的OAuth2Authentication(认证的用户信息)
OAuth2Authentication readAuthentication(OAuth2AccessToken token);
// 根据AccessToken字符串查询对应的OAuth2Authentication(认证的用户信息)
OAuth2Authentication readAuthentication(String token);
//存储AccessToken对象,建立AccessToken对象与OAuth2Authentication对象(认证的用户信息)的关联关系
void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication);
//根据AccessToken字符串查询对应的token对象
OAuth2AccessToken readAccessToken(String tokenValue);
//移除AccessToken对象
void removeAccessToken(OAuth2AccessToken token);
//存储刷新Token,建立refreshToken 与 OAuth2Authentication(认证的用户信息)之间的关联关系
void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication);
//根据RefreshToken字符串查询对应的token对象
OAuth2RefreshToken readRefreshToken(String tokenValue);
//根据RefreshToken对象查询对应的OAuth2Authentication(认证的用户信息)
OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token);
//移除OAuth2RefreshToken 对象
void removeRefreshToken(OAuth2RefreshToken token);
//根据OAuth2RefreshToken 对象,移除关联的AccessToken对象
void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken);
//根据OAuth2Authentication 对象查询OAuth2AccessToken 对象
OAuth2AccessToken getAccessToken(OAuth2Authentication authentication);
//根据clientId + username 查询OAuth2AccessToken集合
Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName);
//根据clientId 查询OAuth2AccessToken集合
Collection<OAuth2AccessToken> findTokensByClientId(String clientId);
}
TokenStore 接口默认提供了如上图所示的实现类,其中,
在TokenStore 接口的实现类中,InMemoryTokenStore 、JdbcTokenStore 和 RedisTokenStore 三个实现类是需要实现token数据的存储工作的,无非是存储的位置不一样,这里以InMemoryTokenStore 实现类为例,分析对应方法是如何实现的。
首先,InMemoryTokenStore 是把数据存储到内存中的,通过定义一些变量进行存储访问token、刷新token、用户认证信息、客户端信息等数据间的关系,实现存储的变量如下所示:
public class InMemoryTokenStore implements TokenStore {
//维护accessToken字符串与OAuth2AccessToken对象的关系
private final ConcurrentHashMap<String, OAuth2AccessToken> accessTokenStore = new ConcurrentHashMap<String, OAuth2AccessToken>();
//维护OAuth2Authentication与OAuth2AccessToken对象的关系,其中key值是OAuth2Authentication对象经过处理得到得字符串
private final ConcurrentHashMap<String, OAuth2AccessToken> authenticationToAccessTokenStore = new ConcurrentHashMap<String, OAuth2AccessToken>();
//维护用户名 与 OAuth2AccessToken对象的关系,一个用户可能对应多个OAuth2AccessToken对象
private final ConcurrentHashMap<String, Collection<OAuth2AccessToken>> userNameToAccessTokenStore = new ConcurrentHashMap<String, Collection<OAuth2AccessToken>>();
//维护clientId 与 OAuth2AccessToken对象的关系,一个clientId可能对应多个OAuth2AccessToken对象
private final ConcurrentHashMap<String, Collection<OAuth2AccessToken>> clientIdToAccessTokenStore = new ConcurrentHashMap<String, Collection<OAuth2AccessToken>>();
//维护refreshToken字符串与OAuth2AccessToken对象的关系
private final ConcurrentHashMap<String, OAuth2RefreshToken> refreshTokenStore = new ConcurrentHashMap<String, OAuth2RefreshToken>();
//维护AccessToken对象value值与RefreshToken对象的value值的关系
private final ConcurrentHashMap<String, String> accessTokenToRefreshTokenStore = new ConcurrentHashMap<String, String>();
//维护AccessToken字符串与OAuth2Authentication的关系
private final ConcurrentHashMap<String, OAuth2Authentication> authenticationStore = new ConcurrentHashMap<String, OAuth2Authentication>();
//维护refreshToken字符串与OAuth2Authentication的关系
private final ConcurrentHashMap<String, OAuth2Authentication> refreshTokenAuthenticationStore = new ConcurrentHashMap<String, OAuth2Authentication>();
//维护refreshToken字符串与AccessToken字符串的关系
private final ConcurrentHashMap<String, String> refreshTokenToAccessTokenStore = new ConcurrentHashMap<String, String>();
//维护具有实现要求的token对象
private final DelayQueue<TokenExpiry> expiryQueue = new DelayQueue<TokenExpiry>();
private final ConcurrentHashMap<String, TokenExpiry> expiryMap = new ConcurrentHashMap<String, TokenExpiry>();
//用来根据用户认证信息生产对应key值的工具类
private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
}
InMemoryTokenStore 实现类实现了接口中定义的方法,其实归根结底就是维护这些数据间的关系,通过变量进行维护,而在基于JDBC的方式中,只需要一张表维护,而这里需要几个字段属性配合使用,具体实现如下所示:
查询OAuth2Authentication(认证的用户信息),提供了如下两个方法。
//根据AccessToken对象查询对应的OAuth2Authentication(认证的用户信息),实际调用了readAuthentication(String token)实现
public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
return readAuthentication(token.getValue());
}
// 根据AccessToken字符串查询对应的OAuth2Authentication(认证的用户信息)
public OAuth2Authentication readAuthentication(String token) {
return this.authenticationStore.get(token);
}
移除acessToken对象。
public void removeAccessToken(OAuth2AccessToken accessToken) {
removeAccessToken(accessToken.getValue());
}
//需要移除所有关联的acessToken对象
public void removeAccessToken(String tokenValue) {
//移除accessTokenStore中存在的acessToken对象
OAuth2AccessToken removed = this.accessTokenStore.remove(tokenValue);
//移除accessTokenToRefreshTokenStore中的acessToken
this.accessTokenToRefreshTokenStore.remove(tokenValue);
//移除authenticationStore中的acessToken,并获取OAuth2Authentication对象,后续根据clientId、username移除对应的acessToken
OAuth2Authentication authentication = this.authenticationStore.remove(tokenValue);
if (authentication != null) {
//根据OAuth2Authentication 生成的key移除AccessToken
this.authenticationToAccessTokenStore.remove(authenticationKeyGenerator.extractKey(authentication));
Collection<OAuth2AccessToken> tokens;
String clientId = authentication.getOAuth2Request().getClientId();
//获取clientId+username对应的OAuth2AccessToken集合
tokens = this.userNameToAccessTokenStore.get(getApprovalKey(clientId, authentication.getName()));
if (tokens != null) {
//移除上述集合中的值为tokenValue的OAuth2AccessToken 对象
tokens.remove(removed);
}
//同理,移除clientIdToAccessTokenStore中的值为tokenValue的OAuth2AccessToken 对象
tokens = this.clientIdToAccessTokenStore.get(clientId);
if (tokens != null) {
tokens.remove(removed);
}
//根据OAuth2Authentication 生成的key移除AccessToken
this.authenticationToAccessTokenStore.remove(authenticationKeyGenerator.extractKey(authentication));
}
}
维护OAuth2AccessToken对象与OAuth2Authentication对象的关系,定期(默认每存储1000次就会移除带有时限的token)清理过期token,为了清除过期token,维护了一个expiryQueue队列。
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
//定期清理缓存中过期的token数据,实际调用了removeAccessToken()方法时限,同时维护expiryQueue队列
if (this.flushCounter.incrementAndGet() >= this.flushInterval) {
//维护expiryQueue队列,并清除对应的 token数据
flush();
//重新计时
this.flushCounter.set(0);
}
//下面代码主要就是操作对应的变量,维护token与其他对象的关系
this.accessTokenStore.put(token.getValue(), token);
this.authenticationStore.put(token.getValue(), authentication);
this.authenticationToAccessTokenStore.put(authenticationKeyGenerator.extractKey(authentication), token);
if (!authentication.isClientOnly()) {
addToCollection(this.userNameToAccessTokenStore, getApprovalKey(authentication), token);
}
addToCollection(this.clientIdToAccessTokenStore, authentication.getOAuth2Request().getClientId(), token);
//如果是有时限要求的token,需要放到队列进行管理
if (token.getExpiration() != null) {
TokenExpiry expiry = new TokenExpiry(token.getValue(), token.getExpiration());
// Remove existing expiry for this token if present
expiryQueue.remove(expiryMap.put(token.getValue(), expiry));
this.expiryQueue.put(expiry);
}
//如果存在刷新token,就维护刷新token与accessToken的对应关系
if (token.getRefreshToken() != null && token.getRefreshToken().getValue() != null) {
this.refreshTokenToAccessTokenStore.put(token.getRefreshToken().getValue(), token.getValue());
this.accessTokenToRefreshTokenStore.put(token.getValue(), token.getRefreshToken().getValue());
}
}
根据AccessToken的value值查询对应的token对象。其实就是通过accessTokenStore变量直接获取即可,代码非常简单,如下所示:
public OAuth2AccessToken readAccessToken(String tokenValue) {
return this.accessTokenStore.get(tokenValue);
}
存储刷新Token,建立refreshToken 与 OAuth2Authentication(认证的用户信息)之间的关联关系,其实就是维护对应的变量,实现非常简单,如下所示:
public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
this.refreshTokenStore.put(refreshToken.getValue(), refreshToken);
this.refreshTokenAuthenticationStore.put(refreshToken.getValue(), authentication);
}
根据RefreshToken的value值查询对应的刷新token对象,实现如下:
public OAuth2RefreshToken readRefreshToken(String tokenValue) {
return this.refreshTokenStore.get(tokenValue);
}
根据RefreshToken对象查询对应的OAuth2Authentication(认证的用户信息),实现如下:
public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
return readAuthenticationForRefreshToken(token.getValue());
}
public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
return this.refreshTokenAuthenticationStore.get(token);
}
移除OAuth2RefreshToken 对象,实现如下:
public void removeRefreshToken(OAuth2RefreshToken refreshToken) {
removeRefreshToken(refreshToken.getValue());
}
public void removeRefreshToken(String tokenValue) {
this.refreshTokenStore.remove(tokenValue);
this.refreshTokenAuthenticationStore.remove(tokenValue);
this.refreshTokenToAccessTokenStore.remove(tokenValue);
}
据OAuth2RefreshToken 对象,移除关联的AccessToken对象,实现如下:
public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
removeAccessTokenUsingRefreshToken(refreshToken.getValue());
}
private void removeAccessTokenUsingRefreshToken(String refreshToken) {
String accessToken = this.refreshTokenToAccessTokenStore.remove(refreshToken);
if (accessToken != null) {
removeAccessToken(accessToken);
}
}
根据OAuth2Authentication 对象查询OAuth2AccessToken 对象,实现如下:
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
//查询OAuth2Authentication 对应的key值
String key = authenticationKeyGenerator.extractKey(authentication);
//查询对应的OAuth2AccessToken 对象
OAuth2AccessToken accessToken = authenticationToAccessTokenStore.get(key);
//根据查询的accessToken 反查对应的OAuth2Authentication 对象,然后对比key是否已知,不一致,重新再维护对应关系,避免下次无法被查询到
if (accessToken != null&& !key.equals(authenticationKeyGenerator.extractKey(readAuthentication(accessToken.getValue())))) {
storeAccessToken(accessToken, authentication);
}
return accessToken;
}
前者,根据clientId + username 查询OAuth2AccessToken集合;后者,根据clientId 查询OAuth2AccessToken集合。
public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
Collection<OAuth2AccessToken> result = userNameToAccessTokenStore.get(getApprovalKey(clientId, userName));
return result != null ? Collections.<OAuth2AccessToken> unmodifiableCollection(result) : Collections
.<OAuth2AccessToken> emptySet();
}
public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
Collection<OAuth2AccessToken> result = clientIdToAccessTokenStore.get(clientId);
return result != null ? Collections.<OAuth2AccessToken> unmodifiableCollection(result) : Collections
.<OAuth2AccessToken> emptySet();
}
这里分析了TokenStore接口的实现类InMemoryTokenStore,其实归根结底就是维护访问token、刷新token、用户认证信息、客户端信息等数据间的关系,并实现这些数据的存储(持久化)工作,而JdbcTokenStore 、RedisTokenStore 本质上也是实现这些数据的持久化工作,只是因为存储方式的不同,实现有所区别而已,这里不再重复。但是关于JwtTokenStore 的实现确实有所不同的,该实现类没有实现token的存储,所有数据都是通过token进行转化而来,这样带来了实现方式的不同,下一节我们专门来分析JwtTokenStore 是如何实现和使用的。