參考資料:
在OAuth 2中模仿DefaultTokenServices寫一個新的tokenServices來提供個性化服務
https://my.oschina.net/u/3768341/blog/2998273
Spring Security OAuth 2.x的刷新token方法自定義修改
使用maven依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>1.3.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>2.0.9.RELEASE</version> </dependency>
需求:
舊項目的其中一個前端頁面會頻繁調用token刷新方法。
刷新的token接口會返回一個新的token令牌值給前端,舊的token令牌作廢掉。
刷新接口時Oauth2框架自帶的接口
/oauth/token?access_token=8192be8f-4564-42cc-9f0f-7049be5ee085&grant_type=refresh_token&refresh_token=4f9ad8f9-885c-4cab-9f39-dc18887b526a&scope=read
請求參數:
access_token: af917135-211e-4dc4-8403-e436a414f7da grant_type: refresh_token refresh_token: 4c6f36e8-fb51-44d5-8f87-1ce7e55aa504 scope: read
返回數據:
access_token: "fcb856b1-c325-46cd-bad4-f5d423688a5d" expires_in: 1799 refresh_token: "4c6f36e8-fb51-44d5-8f87-1ce7e55aa504" scope: "read" token_type: "bearer"
而前端頁面可以打開新的窗口,這個新窗口也需要用到token訪問后端。
到這個新窗口也觸發到某一個前端頁面的刷新token機制時,會調用token刷新接口。
導致 新窗口的token值變更的,而舊窗口的token值沒有變
舊窗口再次攜帶舊token請求后端時因為token無效,重定向跳轉到的登錄頁面。
解決方法:
方法1、調用刷新token方法時,不進行token更新,返回原來的token內容。
可以避免兩個窗口的token不一樣導致跳轉到登錄頁面。
本地調式代碼后,發現刷新token的最終方法是調用 DefaultTokenServices類的刷新token方法
OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
網上搜索資料,可以在配置時,自定義的tokenService代替默認實現類。
具體配置類,主要查看第8行,配置endpoints
1 @Configuration 2 @EnableAuthorizationServer 3 public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { 4 5 @Override 6 public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 7 // 自定義tokenservice方法 8 endpoints.tokenServices(tokenServices(endpoints)); 9 endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler) 10 // .reuseRefreshTokens(false) 11 .authenticationManager(authenticationManager).userDetailsService(defaultUsersDetailsService); 12 } 13 14 private MyTokenService tokenServices(AuthorizationServerEndpointsConfigurer endpoints) { 15 MyTokenService tokenServices = new MyTokenService(); 16 tokenServices.setTokenStore(tokenStore); 17 tokenServices.setSupportRefreshToken(true);//支持刷新token 18 tokenServices.setReuseRefreshToken(true); 19 tokenServices.setClientDetailsService(endpoints.getClientDetailsService()); 20 tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer()); 21 addUserDetailsService(tokenServices, defaultUsersDetailsService); 22 return tokenServices; 23 } 24 25 private void addUserDetailsService(MyTokenService tokenServices, UserDetailsService userDetailsService) { 26 if (userDetailsService != null) { 27 PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider(); 28 provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>( 29 userDetailsService)); 30 tokenServices.setAuthenticationManager(new ProviderManager(Arrays.asList(provider))); 31 } 32 }
自定義的類MyTokenService是復制 DefaultTokenService代碼
修改了刷新token的方法
MyTokenService.java
1 public class MyTokenService implements AuthorizationServerTokenServices, ResourceServerTokenServices, 2 ConsumerTokenServices, InitializingBean { 3 /** 4 * 修改刷新token方法,只更新token時間,不更換舊的token值 5 * @param refreshTokenValue 6 * @param tokenRequest 7 * @return 8 * @throws AuthenticationException 9 */ 10 @Override 11 @Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class}) 12 public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest) 13 throws AuthenticationException { 14 15 。。。前面沒動 16 17 OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken); 18 // 保留token原值 19 ((DefaultOAuth2AccessToken)accessToken).setValue(tokenRequest.getRequestParameters().get("access_token")); 20 tokenStore.storeAccessToken(accessToken, authentication); 21 if (!reuseRefreshToken) { 22 tokenStore.storeRefreshToken(refreshToken, authentication); 23 } 24 return accessToken; 25 }
重新啟動項目,刷新token接口調用后,返回的還是原來的token值