在OAuth2中 自定义tokenServices来提供个性化服务,每次刷新token并让原token在5分钟内有效
这样写有几个好处:
- 不需要使用拦截器来让设备异地登录失效,大大提升吞吐量
- 每次登录都刷新了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