起因&环境

环境:

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

追查

首先,打开了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语句

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


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