這樣寫有幾個好處:
- 不需要使用攔截器來讓設備異地登錄失效,大大提升吞吐量
- 每次登錄都刷新了access_token,並且加滿了過期時間,不會出現過期時間到了要重新登錄的問題。
- 可以自定義在獲取新token后,讓原token5分鍾內仍有效
直接復制DefaultTokenServices代碼進行修改
所有的代碼保留,唯獨要修改的是createAccessToken這個方法
@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 { // modified by pancg 2020-03-02 // 根據當前開放平台功能設計調整為每次獲取新token並讓老token在5分鍾內有效 // 1.這里是原來的實現 // Re-store the access token in case the authentication has changed //tokenStore.storeAccessToken(existingAccessToken, authentication); //return existingAccessToken; // 2.讓原token在token更新后5分鍾內仍有效 // Re-store the access token in case the authentication has changed tokenStore.storeAccessToken(createAccessTokenInNextFiveMinute(existingAccessToken, authentication), authentication); // 3.下面是創建新token並返回 // 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; } } // 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; }
/** * @description 創建一個使原token在接下來5分鍾有效的token * @added by pancg * @param * @return */ private OAuth2AccessToken createAccessTokenInNextFiveMinute(OAuth2AccessToken existingAccessToken, OAuth2Authentication authentication) { final int validitySeconds = 5 * 60; DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(existingAccessToken.getValue()); //DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString()); //int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request()); if (validitySeconds > 0) { token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L))); } token.setRefreshToken(existingAccessToken.getRefreshToken()); token.setScope(authentication.getOAuth2Request().getScope()); //return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token; return token; }
最后在AuthorizationServerConfig增加如下內容,其中endpoints.tokenServices(tokenServices(endpoints));就是把我們新寫的SingleTokenServices給配置進來。
@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenServices(tokenServices(endpoints)); endpoints.authenticationManager(this.authenticationManager); endpoints.tokenStore(tokenStore()); // 授權碼模式下,code存儲 // endpoints.authorizationCodeServices(new JdbcAuthorizationCodeServices(dataSource)); endpoints.authorizationCodeServices(redisAuthorizationCodeServices); if (storeWithJwt) { endpoints.accessTokenConverter(accessTokenConverter()); } } private SingleTokenServices tokenServices(AuthorizationServerEndpointsConfigurer endpoints) { SingleTokenServices tokenServices = new SingleTokenServices(); tokenServices.setTokenStore(tokenStore()); tokenServices.setSupportRefreshToken(true);//支持刷新token tokenServices.setReuseRefreshToken(true); tokenServices.setClientDetailsService(endpoints.getClientDetailsService()); tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer()); addUserDetailsService(tokenServices, this.userDetailsService); return tokenServices; } private void addUserDetailsService(SingleTokenServices tokenServices, UserDetailsService userDetailsService) { if (userDetailsService != null) { PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider(); provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>( userDetailsService)); tokenServices.setAuthenticationManager(new ProviderManager(Arrays.asList(provider))); } }
參考:https://my.oschina.net/u/3768341/blog/2998273