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; } }
// 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 modifiedrefreshToken = accessToken.getRefreshToken();if (refreshToken != null) {tokenStore.storeRefreshToken(refreshToken, authentication);}return accessToken;
}
③代码解释<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```java@Override@Transactionalpublic OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);OAuth2RefreshToken refreshToken = null;//清除原始accessToken和refreshTokenif (existingAccessToken != null) {if (existingAccessToken.getRefreshToken() != null) {refreshToken = existingAccessToken.getRefreshToken();tokenStore.removeRefreshToken(refreshToken);}tokenStore.removeAccessToken(existingAccessToken);}refreshToken = createRefreshToken(authentication);OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);tokenStore.storeAccessToken(accessToken, authentication);// In case it was modifiedrefreshToken = accessToken.getRefreshToken();if (refreshToken != null) {tokenStore.storeRefreshToken(refreshToken, authentication);}return accessToken;}
4.修改后的特征
1.同一用户重复登录会生成新的access_token和refresh_token
2.同一个用户只有最新的access_token、refresh_token有效,其他的都会失效。
