1.文档内容

本文档针对普通令牌并用Redis存储的这种方式的底层源码的生成解析方式,并改造底层代码适应现有需求

2.使用Redis存储默认的特征存在的问题

问题:同一个用户多次登录,会返回同一个access_token,这样无法实现一个登录顶替的功能
解决思路:让用户二次登录后重新生成access_token,并让前一次登录生成的access_token立即失效。

3.阅读Access_Token生成源码

①控制access_token逻辑生产的源码在DefaultTokenServices类中
②主要方法是以下的代码:

```java @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; } }

  1. // Only create a new refresh token if there wasn't an existing one
  2. // associated with an expired access token.
  3. // Clients might be holding existing refresh tokens, so we re-use it in
  4. // the case that the old access token
  5. // expired.
  6. if (refreshToken == null) {
  7. refreshToken = createRefreshToken(authentication);
  8. }
  9. // But the refresh token itself might need to be re-issued if it has
  10. // expired.
  11. else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
  12. ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken;
  13. if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
  14. refreshToken = createRefreshToken(authentication);
  15. }
  16. }
  17. OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
  18. tokenStore.storeAccessToken(accessToken, authentication);
  19. // In case it was modified
  20. refreshToken = accessToken.getRefreshToken();
  21. if (refreshToken != null) {
  22. tokenStore.storeRefreshToken(refreshToken, authentication);
  23. }
  24. return accessToken;

}

  1. ③代码解释<br />a.如果redis有此用户登录时生成的access_token,并且没有过期,就将上一次用户登录生成的access_token<br />b.如果没有此用户登录的access_token则生成新的access_token<br />④解决方式<br />重新创建一个类,模拟DefaultTokenServices,编写自己的TokenServices,并让SpringSecurityOauth2使用我自己的这套生成access_token的逻辑,其他方法不做修改即可。<br />具体逻辑:<br />①当用户生成token,我会从redis中查询是否有未过期的用户登录access_token<br />②如果有,清除之前的用户登录access_token<br />③生成新的access_token
  2. ```java
  3. @Override
  4. @Transactional
  5. public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
  6. OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
  7. OAuth2RefreshToken refreshToken = null;
  8. //清除原始accessToken和refreshToken
  9. if (existingAccessToken != null) {
  10. if (existingAccessToken.getRefreshToken() != null) {
  11. refreshToken = existingAccessToken.getRefreshToken();
  12. tokenStore.removeRefreshToken(refreshToken);
  13. }
  14. tokenStore.removeAccessToken(existingAccessToken);
  15. }
  16. refreshToken = createRefreshToken(authentication);
  17. OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
  18. tokenStore.storeAccessToken(accessToken, authentication);
  19. // In case it was modified
  20. refreshToken = accessToken.getRefreshToken();
  21. if (refreshToken != null) {
  22. tokenStore.storeRefreshToken(refreshToken, authentication);
  23. }
  24. return accessToken;
  25. }

4.修改后的特征

1.同一用户重复登录会生成新的access_token和refresh_token
2.同一个用户只有最新的access_token、refresh_token有效,其他的都会失效。