banner
ZetoHkr

ZetoHkr

摸🐟从未停止,努力从未开始
github

Spring security oauth重复签发令牌的坑

起因 & 环境#

环境:

在工作的时候,发现了个问题,为什么相同的账号在不同的地方会用相同的令牌?这在正常的情况下不是合理的。如果有新的登录,我们也许把原有令牌清除,以免别人拿去利用。

追查#

首先,打开了 OAuth2 授权服务器的代码,发现并没有什么太多令牌生成相关的逻辑,只有AuthorizationServerConfiguration里面有这段代码:

@Bean
@Primary
public DefaultTokenServices tokenServices() {
	DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
	defaultTokenServices.setTokenStore(tokenStore());
	defaultTokenServices.setClientDetailsService(clientDetailsService);
	defaultTokenServices.setAccessTokenValiditySeconds(7200);
	defaultTokenServices.setSupportRefreshToken(false);
	return defaultTokenServices;
}

嗯?令牌服务,关于令牌的只有这部分,所以我们需要继续向下追查,直接去看DefaultTokenServices类的内容,然后就发现了这段代码:

	@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;
	}

其中包含了一句// Re-store the access token in case the authentication has changed,这翻译过来不就是把之前签发的没有过期的令牌重新存了一次,就当作签发新的令牌了,好好好,这么写。

然后我们就需要改写一下这段代码,应该使其失效,然后颁发新的令牌;只需要将这里:

	@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;
			}
		}
		// 其他代码,保持不变,除非有其他修改
	}

改为如下代码:

				tokenStore.removeAccessToken(existingAccessToken);
				// 并删除return语句

这样登录就不会直接签发相同的令牌了。

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。