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();
					// トークンストアは、アクセストークンが削除されるときに
					// リフレッシュトークンを削除することができますが、私たちは
					// 確実にしたいのです...
					tokenStore.removeRefreshToken(refreshToken);
				}
				tokenStore.removeAccessToken(existingAccessToken);
			}
			else {
				// 認証が変更された場合に備えてアクセストークンを再保存する
				tokenStore.storeAccessToken(existingAccessToken, authentication);
				return existingAccessToken;
			}
		}
		// 既存のアクセストークンに関連付けられたリフレッシュトークンがない場合のみ
		// 新しいリフレッシュトークンを作成します。
		// クライアントは既存のリフレッシュトークンを保持している可能性があるため、
		// 古いアクセストークンが
		// 期限切れになった場合に再利用します。
		if (refreshToken == null) {
			refreshToken = createRefreshToken(authentication);
		}
		// ただし、リフレッシュトークン自体は
		// 期限切れの場合は再発行する必要があります。
		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);
		// 変更された場合に備えて
		refreshToken = accessToken.getRefreshToken();
		if (refreshToken != null) {
			tokenStore.storeRefreshToken(refreshToken, authentication);
		}
		return accessToken;
	}

その中に// 認証が変更された場合に備えてアクセストークンを再保存するという行が含まれており、これは以前に発行された未期限切れのトークンを再保存することを意味します。新しいトークンを発行するかのように扱われます。よし、こう書きましょう。

次に、このコードを改訂して、無効にし、新しいトークンを発行する必要があります。ここを変更するだけです:

	@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();
					// トークンストアは、アクセストークンが削除されるときに
					// リフレッシュトークンを削除することができますが、私たちは
					// 確実にしたいのです...
					tokenStore.removeRefreshToken(refreshToken);
				}
				tokenStore.removeAccessToken(existingAccessToken);
			}
			else {
				// ここを変更します
				// 認証が変更された場合に備えてアクセストークンを再保存する
				tokenStore.storeAccessToken(existingAccessToken, authentication);
				return existingAccessToken;
			}
		}
		// 他のコードは変更せず、そのままにします
	}

これを次のコードに変更します:

				tokenStore.removeAccessToken(existingAccessToken);
				// return文を削除します

これにより、ログイン時に同じトークンが直接発行されなくなります。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。