SpringSecurity OAuth2中关于创建Token的实现类DefaultTokenServices及TokenStore实现类的详解

1、前言

  在《SpringSecurity OAuth2 获取Token端点TokenEndpoint、Token授权TokenGranter接口 详解》中,在TokenGranter接口的几个实现类中,真正实现token生成的其实是AuthorizationServerTokenServices对象。这一篇,我们专门来分析AuthorizationServerTokenServices是如何实现token生成的。

2、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属性的值不能为空。

2.1、createAccessToken()方法 创建AccessToken

  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()方法

   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()方法

  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;
}
2.2、refreshAccessToken()方法 刷新accessToken

  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()方法

  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;
}
2.3、getAccessToken()方法 查询accessToken

  getAccessToken()方法,主要提供了查询accessToken的功能,实际就是通过tokenStore对象,根据OAuth2Authentication参数查询OAuth2AccessToken对象,实现如下:

public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
	return tokenStore.getAccessToken(authentication);
}

3、TokenStore

  在AuthorizationServerTokenServices 中,无论是createAccessToken()、refreshAccessToken()方法还是getAccessToken()方法,都是基于TokenStore对象实现token的存储和查询功能的,这里我们专门学习一下TokenStore的具体实现。

3.1、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);
}
3.2、TokenStore 层级结构

在这里插入图片描述
  TokenStore 接口默认提供了如上图所示的实现类,其中,

  1. JdbcTokenStore 提供了基于JDBC存储Token的实现,其中内置了很多查询、新增、删除数据的SQL语句
  2. RedisTokenStore 提供了基于Redis存储Token的实现,需要引入Redis的支持
  3. InMemoryTokenStore 提供了基于内存存储Token的实现,其中定义了很多变量用于存储Token与认证用户间的关系
  4. JwtTokenStore 提供了基于JWT存储Token的实现,这里不需要真正的存储token,因为用户认证信息已经在token中进行存储了
  5. JwkTokenStore
3.3、InMemoryTokenStore 实现类

  在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的方式中,只需要一张表维护,而这里需要几个字段属性配合使用,具体实现如下所示:

readAuthentication()方法

  查询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);
}
removeAccessToken()方法

  移除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));
	}
}
storeAccessToken()方法

  维护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());
	}
}
readAccessToken()方法

  根据AccessToken的value值查询对应的token对象。其实就是通过accessTokenStore变量直接获取即可,代码非常简单,如下所示:

public OAuth2AccessToken readAccessToken(String tokenValue) {
	return this.accessTokenStore.get(tokenValue);
}
storeRefreshToken()方法

  存储刷新Token,建立refreshToken 与 OAuth2Authentication(认证的用户信息)之间的关联关系,其实就是维护对应的变量,实现非常简单,如下所示:

public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
	this.refreshTokenStore.put(refreshToken.getValue(), refreshToken);
	this.refreshTokenAuthenticationStore.put(refreshToken.getValue(), authentication);
}
readRefreshToken()方法

  根据RefreshToken的value值查询对应的刷新token对象,实现如下:

public OAuth2RefreshToken readRefreshToken(String tokenValue) {
	return this.refreshTokenStore.get(tokenValue);
}
readAuthenticationForRefreshToken()方法

  根据RefreshToken对象查询对应的OAuth2Authentication(认证的用户信息),实现如下:

public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
	return readAuthenticationForRefreshToken(token.getValue());
}

public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
	return this.refreshTokenAuthenticationStore.get(token);
}
removeRefreshToken()方法

  移除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);
}
removeAccessTokenUsingRefreshToken()方法

  据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);
	}
}
getAccessToken()方法

  根据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;
}
findTokensByClientIdAndUserName()方法、findTokensByClientId()方法

  前者,根据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();
}

4、最后

  这里分析了TokenStore接口的实现类InMemoryTokenStore,其实归根结底就是维护访问token、刷新token、用户认证信息、客户端信息等数据间的关系,并实现这些数据的存储(持久化)工作,而JdbcTokenStore 、RedisTokenStore 本质上也是实现这些数据的持久化工作,只是因为存储方式的不同,实现有所区别而已,这里不再重复。但是关于JwtTokenStore 的实现确实有所不同的,该实现类没有实现token的存储,所有数据都是通过token进行转化而来,这样带来了实现方式的不同,下一节我们专门来分析JwtTokenStore 是如何实现和使用的。

  • 9
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Spring Security OAuth2 Authorization Server可以通过Token增强器(Token Enhancer)来为授权后返回的Access Token添加额外的信息。Token增强器是一个接口,它接收一个Access Token并返回一个增强后的Access Token。在Authorization Server配置,可以通过调用tokenEnhancer()方法来设置Token增强器,示例代码如下: ```java @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; @Autowired private DataSource dataSource; @Bean public TokenStore tokenStore() { return new JdbcTokenStore(dataSource); } @Bean public JwtAccessTokenConverter accessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("123456"); return converter; } @Bean public TokenEnhancer tokenEnhancer() { return new CustomTokenEnhancer(); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.jdbc(dataSource); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService) .tokenStore(tokenStore()) .accessTokenConverter(accessTokenConverter()) .tokenEnhancer(tokenEnhancer()); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer.tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()") .allowFormAuthenticationForClients(); } } ``` 在上面的代码,CustomTokenEnhancer是一个自定义的Token增强器,它可以在Access Token添加额外的信息。示例代码如下: ```java public class CustomTokenEnhancer implements TokenEnhancer { @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { Map<String, Object> additionalInfo = new HashMap<>(); additionalInfo.put("organization", authentication.getName() + "@test.com"); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); return accessToken; } } ``` 在上面的代码,我们向Access Token添加了一个名为“organization”的信息,它的值为当前用户的用户名加上@test.com。这种方式可以为Access Token添加任何我们需要的信息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姠惢荇者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值