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.framework.common.util.json.JsonUtils;
|
||||||
import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
|
import com.cf.imes.module.system.dal.dataobject.oauth2.OAuth2AccessTokenDO;
|
||||||
import com.cf.imes.module.system.dal.redis.RedisKeyConstants;
|
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.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
@@ -15,6 +17,8 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static com.cf.imes.module.system.dal.redis.RedisKeyConstants.OAUTH2_ACCESS_TOKEN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link OAuth2AccessTokenDO} 的 RedisDAO
|
* {@link OAuth2AccessTokenDO} 的 RedisDAO
|
||||||
*
|
*
|
||||||
@@ -26,11 +30,27 @@ public class OAuth2AccessTokenRedisDAO {
|
|||||||
@Resource
|
@Resource
|
||||||
private StringRedisTemplate stringRedisTemplate;
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不带key获取
|
||||||
|
*
|
||||||
|
* @param accessToken
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public OAuth2AccessTokenDO get(String accessToken) {
|
public OAuth2AccessTokenDO get(String accessToken) {
|
||||||
String redisKey = formatKey(accessToken);
|
String redisKey = formatKey(accessToken);
|
||||||
return JsonUtils.parseObject(stringRedisTemplate.opsForValue().get(redisKey), OAuth2AccessTokenDO.class);
|
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) {
|
public void set(OAuth2AccessTokenDO accessTokenDO) {
|
||||||
String redisKey = formatKey(accessTokenDO.getAccessToken());
|
String redisKey = formatKey(accessTokenDO.getAccessToken());
|
||||||
// 清理多余字段,避免缓存
|
// 清理多余字段,避免缓存
|
||||||
@@ -42,11 +62,26 @@ public class OAuth2AccessTokenRedisDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不带key删除
|
||||||
|
*
|
||||||
|
* @param accessToken
|
||||||
|
*/
|
||||||
public void delete(String accessToken) {
|
public void delete(String accessToken) {
|
||||||
String redisKey = formatKey(accessToken);
|
String redisKey = formatKey(accessToken);
|
||||||
stringRedisTemplate.delete(redisKey);
|
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) {
|
public void deleteList(Collection<String> accessTokens) {
|
||||||
List<String> redisKeys = CollectionUtils.convertList(accessTokens, OAuth2AccessTokenRedisDAO::formatKey);
|
List<String> redisKeys = CollectionUtils.convertList(accessTokens, OAuth2AccessTokenRedisDAO::formatKey);
|
||||||
stringRedisTemplate.delete(redisKeys);
|
stringRedisTemplate.delete(redisKeys);
|
||||||
@@ -67,4 +102,12 @@ public class OAuth2AccessTokenRedisDAO {
|
|||||||
return String.format(RedisKeyConstants.OAUTH2_ACCESS_TOKEN, accessToken);
|
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.mysql.oauth2.OAuth2RefreshTokenMapper;
|
||||||
import com.cf.imes.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO;
|
import com.cf.imes.module.system.dal.redis.oauth2.OAuth2AccessTokenRedisDAO;
|
||||||
import com.cf.imes.module.system.service.organ.OrganService;
|
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.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception0;
|
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 晨丰科技
|
* @author 晨丰科技
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@Slf4j
|
||||||
public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@@ -182,8 +188,10 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
|
|
||||||
// 在Redis中新增Token,除去刷新Token字段
|
// 在Redis中新增Token,除去刷新Token字段
|
||||||
private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2AccessTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
|
private OAuth2AccessTokenDO createOAuth2AccessToken(OAuth2AccessTokenDO refreshTokenDO, OAuth2ClientDO clientDO) {
|
||||||
OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(generateAccessToken())
|
String currentToken = generateAccessToken();
|
||||||
.setUserId(refreshTokenDO.getUserId()).setUserType(refreshTokenDO.getUserType())
|
Long userId = refreshTokenDO.getUserId();
|
||||||
|
OAuth2AccessTokenDO accessTokenDO = new OAuth2AccessTokenDO().setAccessToken(currentToken)
|
||||||
|
.setUserId(userId).setUserType(refreshTokenDO.getUserType())
|
||||||
.setClientId(clientDO.getClientId()).setScopes(refreshTokenDO.getScopes())
|
.setClientId(clientDO.getClientId()).setScopes(refreshTokenDO.getScopes())
|
||||||
.setRefreshToken(refreshTokenDO.getRefreshToken())
|
.setRefreshToken(refreshTokenDO.getRefreshToken())
|
||||||
.setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getAccessTokenValiditySeconds()))
|
.setExpiresTime(LocalDateTime.now().plusSeconds(clientDO.getAccessTokenValiditySeconds()))
|
||||||
@@ -193,9 +201,41 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
|||||||
.setDeptId(refreshTokenDO.getDeptId());
|
.setDeptId(refreshTokenDO.getDeptId());
|
||||||
// 记录到 Redis 中
|
// 记录到 Redis 中
|
||||||
oauth2AccessTokenRedisDAO.set(accessTokenDO);
|
oauth2AccessTokenRedisDAO.set(accessTokenDO);
|
||||||
|
// 异步踢出其他token
|
||||||
|
CompletableFuture.runAsync(() -> {
|
||||||
|
kickOut(userId, currentToken);
|
||||||
|
}).exceptionally(e -> {
|
||||||
|
log.error("[OAuth2TokenServiceImpl] redis 清空token失败, 用户id:{}, 异常:{}", userId, e);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
return accessTokenDO;
|
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() {
|
private static String generateAccessToken() {
|
||||||
|
|||||||
Reference in New Issue
Block a user