参考资料:
在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值