mirror of
http://192.168.1.205:9980/cf_devdept2/cf_imes_server.git
synced 2026-08-12 21:02:08 +08:00
系统登录:非超管支持登陆成功后移除当前用户其他token
This commit is contained in:
+43
@@ -5,6 +5,8 @@ import com.cf.imes.framework.common.util.collection.CollectionUtils;
|
||||
import com.cf.imes.framework.common.util.json.JsonUtils;
|
||||
import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
|
||||
import com.cf.imes.module.system.dal.redis.RedisKeyConstants;
|
||||
import org.springframework.data.redis.core.Cursor;
|
||||
import org.springframework.data.redis.core.ScanOptions;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@@ -15,6 +17,8 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.cf.imes.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
|
||||
|
||||
/**
|
||||
* {@link OAuth2AccessTokenDO} 的 RedisDAO
|
||||
*
|
||||
@@ -26,11 +30,27 @@ public class OAuth2AccessTokenRedisDAO {
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* 不带key获取
|
||||
*
|
||||
* @param accessToken
|
||||
* @return
|
||||
*/
|
||||
public OAuth2AccessTokenDO get(String accessToken) {
|
||||
String redisKey = formatKey(accessToken);
|
||||
return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 带key获取,oauth2_access_token:xxxx
|
||||
*
|
||||
* @param accessToken
|
||||
* @return
|
||||
*/
|
||||
public OAuth2AccessTokenDO getWithKey(String accessToken) {
|
||||
return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(accessToken), OAuth2AccessTokenDO.class);
|
||||
}
|
||||
|
||||
public void set(OAuth2AccessTokenDO accessTokenDO) {
|
||||
String redisKey = formatKey(accessTokenDO.getAccessToken());
|
||||
// 清理多余字段,避免缓存
|
||||
@@ -42,11 +62,26 @@ public class OAuth2AccessTokenRedisDAO {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 不带key删除
|
||||
*
|
||||
* @param accessToken
|
||||
*/
|
||||
public void delete(String accessToken) {
|
||||
String redisKey = formatKey(accessToken);
|
||||
stringRedisTemplate.delete(redisKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 带key删除,oauth2_access_token:xxxx
|
||||
*
|
||||
* @param accessToken
|
||||
* @return
|
||||
*/
|
||||
public void deleteWithKey(String accessToken) {
|
||||
stringRedisTemplate.delete(accessToken);
|
||||
}
|
||||
|
||||
public void deleteList(Collection<String> accessTokens) {
|
||||
List<String> redisKeys = CollectionUtils.convertList(accessTokens, OAuth2AccessTokenRedisDAO::formatKey);
|
||||
stringRedisTemplate.delete(redisKeys);
|
||||
@@ -67,4 +102,12 @@ public class OAuth2AccessTokenRedisDAO {
|
||||
return String.format(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, accessToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建token游标,使用时轮训一次获取两百个token
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Cursor<String> scan(){
|
||||
return stringRedisTemplate.scan(ScanOptions.scanOptions().match(String.format(OAUTH2_ACCESS_TOKEN, "*")).count(200).build());
|
||||
}
|
||||
}
|
||||
|
||||
+42
-2
@@ -17,12 +17,17 @@ import com.cf.imes.module.system.dal.mysql.oauth2.OAuth2AccessTokenMapper;
|
||||
import com.cf.imes.module.system.dal.mysql.oauth2.OAuth2RefreshTokenMapper;
|
||||
import com.cf.imes.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO;
|
||||
import com.cf.imes.module.system.service.organ.OrganService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.redis.core.Cursor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
||||
@@ -34,6 +39,7 @@ import static com.cf.imes.framework.common.util.collection.CollectionUtils.conve
|
||||
* @author 晨丰科技
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||
|
||||
@Resource
|
||||
@@ -182,8 +188,10 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||
|
||||
// 在Redis中新增Token,除去刷新Token字段
|
||||
private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2AccessTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
|
||||
OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
|
||||
.setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType())
|
||||
String currentToken = generateAccessToken();
|
||||
Long userId = refreshTokenDO.getUserId();
|
||||
OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(currentToken)
|
||||
.setUserId(userId).setUserType(refreshTokenDO.getUserType())
|
||||
.setClientId(clientDO.getClientId()).setScopes(refreshTokenDO.getScopes())
|
||||
.setRefreshToken(refreshTokenDO.getRefreshToken())
|
||||
.setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getAccessTokenValiditySeconds()))
|
||||
@@ -193,9 +201,41 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||
.setDeptId(refreshTokenDO.getDeptId());
|
||||
// 记录到 Redis 中
|
||||
oauth2AccessTokenRedisDAO.set(accessTokenDO);
|
||||
// 异步踢出其他token
|
||||
CompletableFuture.runAsync(() -> {
|
||||
kickOut(userId, currentToken);
|
||||
}).exceptionally(e -> {
|
||||
log.error("[OAuth2TokenServiceImpl] redis 清空token失败, 用户id:{}, 异常:{}", userId, e);
|
||||
return null;
|
||||
});
|
||||
return accessTokenDO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 踢出除本次userId登录产生token以外的所有token(不处理超管的超管除外)
|
||||
*
|
||||
* @param userId
|
||||
* @param currentToken
|
||||
*/
|
||||
public void kickOut(Long userId, String currentToken) {
|
||||
List<String> matchKeys = new ArrayList<>();
|
||||
Cursor<String> cursor = oauth2AccessTokenRedisDAO.scan();
|
||||
while (cursor.hasNext()) {
|
||||
matchKeys.add(cursor.next());
|
||||
}
|
||||
cursor.close();
|
||||
if (CollUtil.isNotEmpty(matchKeys)) {
|
||||
for (String key : matchKeys) {
|
||||
// 获取key下的用户信息
|
||||
OAuth2AccessTokenDO oAuth2AccessTokenDO = oauth2AccessTokenRedisDAO.getWithKey(key);
|
||||
if (ObjectUtil.equal(userId, oAuth2AccessTokenDO.getUserId()) && !StringUtils.equals(currentToken, oAuth2AccessTokenDO.getAccessToken()) && Boolean.FALSE.equals(oAuth2AccessTokenDO.getIsSupAdmin())) {
|
||||
// 用户id匹配、非当次登录token、非超管 -> 删除redis中的token缓存
|
||||
oauth2AccessTokenRedisDAO.deleteWithKey(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static String generateAccessToken() {
|
||||
|
||||
Reference in New Issue
Block a user