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語句

這樣登錄就不會直接簽發相同的令牌了。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。