Merge pull request #4784 from dataease/pr@dev@perf_multi_login_cache

perf(登录): 多端登录限制缓存优化
This commit is contained in:
fit2cloud-chenyw 2023-03-14 17:45:40 +08:00 committed by GitHub
commit 5244c65095
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 40 additions and 83 deletions

View File

@ -9,11 +9,9 @@ import com.auth0.jwt.interfaces.Verification;
import com.google.gson.Gson;
import io.dataease.auth.entity.TokenInfo;
import io.dataease.auth.entity.TokenInfo.TokenInfoBuilder;
import io.dataease.commons.exception.DEException;
import io.dataease.commons.model.OnlineUserModel;
import io.dataease.commons.utils.*;
import io.dataease.exception.DataEaseException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.Environment;
@ -23,7 +21,6 @@ import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class JWTUtils {
@ -96,13 +93,9 @@ public class JWTUtils {
}
private static Set<OnlineUserModel> filterValid(Set<OnlineUserModel> userModels) {
Set<OnlineUserModel> models = userModels.stream().filter(JWTUtils::tokenValid).collect(Collectors.toSet());
return models;
}
private static String models2Json(Set<OnlineUserModel> models, boolean withCurToken, String token) {
private static String models2Json(OnlineUserModel model, boolean withCurToken, String token) {
Set<OnlineUserModel> models = new LinkedHashSet<>();
models.add(model);
Gson gson = new Gson();
List<OnlineUserModel> userModels = models.stream().map(item -> {
item.setToken(null);
@ -120,31 +113,25 @@ public class JWTUtils {
}
public static String seizeSign(Long userId, String token) {
Set<OnlineUserModel> userModels = Optional.ofNullable(TokenCacheUtils.onlineUserTokens(userId)).orElse(new LinkedHashSet<>());
userModels.stream().forEach(model -> {
TokenCacheUtils.add(model.getToken(), userId);
});
userModels.clear();
OnlineUserModel curModel = TokenCacheUtils.buildModel(token);
userModels.add(curModel);
TokenCacheUtils.resetOnlinePools(userId, userModels);
Optional.ofNullable(TokenCacheUtils.onlineUserToken(userId)).ifPresent(model -> TokenCacheUtils.add(model.getToken(), userId));
TokenCacheUtils.add2OnlinePools(token, userId);
return IPUtils.get();
}
public static String sign(TokenInfo tokenInfo, String secret, boolean writeOnline) {
Long userId = tokenInfo.getUserId();
String multiLoginType = null;
if (writeOnline && StringUtils.equals("1", (multiLoginType = TokenCacheUtils.multiLoginType()))) {
Set<OnlineUserModel> userModels = TokenCacheUtils.onlineUserTokens(userId);
if (CollectionUtils.isNotEmpty(userModels) && CollectionUtils.isNotEmpty((userModels = filterValid(userModels)))) {
TokenCacheUtils.resetOnlinePools(userId, userModels);
OnlineUserModel userModel = TokenCacheUtils.onlineUserToken(userId);
if (ObjectUtils.isNotEmpty(userModel) && tokenValid(userModel)) {
HttpServletResponse response = ServletUtils.response();
Cookie cookie_token = new Cookie("MultiLoginError1", models2Json(userModels, false, null));cookie_token.setPath("/");
Cookie cookie_token = new Cookie("MultiLoginError1", models2Json(userModel, false, null));
cookie_token.setPath("/");
cookie_token.setPath("/");
response.addCookie(cookie_token);
DataEaseException.throwException("MultiLoginError1");
}
}
if (ObjectUtils.isEmpty(expireTime)) {
expireTime = CommonBeanFactory.getBean(Environment.class).getProperty("dataease.login_timeout", Long.class, 480L);
@ -156,23 +143,21 @@ public class JWTUtils {
.withClaim("username", tokenInfo.getUsername())
.withClaim("userId", userId);
String sign = builder.withExpiresAt(date).sign(algorithm);
if (writeOnline && !StringUtils.equals("0", multiLoginType)) {
if (StringUtils.equals("2", multiLoginType)) {
Set<OnlineUserModel> userModels = TokenCacheUtils.onlineUserTokens(userId);
if (CollectionUtils.isNotEmpty(userModels) && CollectionUtils.isNotEmpty((userModels = filterValid(userModels)))) {
HttpServletResponse response = ServletUtils.response();
Cookie cookie_token = new Cookie("MultiLoginError2", models2Json(userModels, true, sign));
cookie_token.setPath("/");
response.addCookie(cookie_token);
userModels = userModels.stream().filter(mode -> !StringUtils.equals(mode.getToken(), sign)).collect(Collectors.toSet());
TokenCacheUtils.resetOnlinePools(userId, userModels);
DataEaseException.throwException("MultiLoginError");
}
if (StringUtils.equals("2", multiLoginType)) {
OnlineUserModel userModel = TokenCacheUtils.onlineUserToken(userId);
if (ObjectUtils.isNotEmpty(userModel) && tokenValid(userModel)) {
HttpServletResponse response = ServletUtils.response();
Cookie cookie_token = new Cookie("MultiLoginError2", models2Json(userModel, true, sign));
cookie_token.setPath("/");
response.addCookie(cookie_token);
DataEaseException.throwException("MultiLoginError");
}
}
if (writeOnline && !StringUtils.equals("0", multiLoginType)) {
TokenCacheUtils.add2OnlinePools(sign, userId);
}
return sign;
}

View File

@ -1,5 +1,6 @@
package io.dataease.commons.utils;
import com.google.gson.Gson;
import io.dataease.commons.model.OnlineUserModel;
import io.dataease.listener.util.CacheUtils;
import io.dataease.service.system.SystemParameterService;
@ -7,12 +8,9 @@ import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@ -28,6 +26,8 @@ public class TokenCacheUtils {
private static Long expTime;
private static Gson gson = new Gson();
@Value("${spring.cache.type:ehcache}")
public void setCacheType(String cacheType) {
TokenCacheUtils.cacheType = cacheType;
@ -62,17 +62,6 @@ public class TokenCacheUtils {
CacheUtils.flush(KEY);
}
public static void remove(String token) {
if (useRedis()) {
RedisTemplate redisTemplate = (RedisTemplate) CommonBeanFactory.getBean("redisTemplate");
String key = KEY + token;
if (redisTemplate.hasKey(key)) {
redisTemplate.delete(key);
}
return;
}
CacheUtils.remove(KEY, token);
}
public static boolean invalid(String token) {
if (useRedis()) {
@ -83,36 +72,17 @@ public class TokenCacheUtils {
return ObjectUtils.isNotEmpty(sys_token_store) && StringUtils.isNotBlank(sys_token_store.toString());
}
public static void resetOnlinePools(Long userId, Set<OnlineUserModel> sets) {
if (useRedis()) {
RedisTemplate redisTemplate = (RedisTemplate) CommonBeanFactory.getBean("redisTemplate");
redisTemplate.delete(ONLINE_TOKEN_POOL_KEY + userId);
SetOperations setOperations = redisTemplate.opsForSet();
Object[] modelArray = sets.stream().toArray();
setOperations.add(ONLINE_TOKEN_POOL_KEY + userId, modelArray);
return;
}
CacheUtils.removeAll(ONLINE_TOKEN_POOL_KEY);
CacheUtils.put(ONLINE_TOKEN_POOL_KEY, userId, sets, null, null);
CacheUtils.flush(ONLINE_TOKEN_POOL_KEY);
}
public static void add2OnlinePools(String token, Long userId) {
OnlineUserModel model = buildModel(token);
if (useRedis()) {
RedisTemplate redisTemplate = (RedisTemplate) CommonBeanFactory.getBean("redisTemplate");
SetOperations setOperations = redisTemplate.opsForSet();
setOperations.add(ONLINE_TOKEN_POOL_KEY + userId, buildModel(token));
ValueOperations valueOperations = cacheHandler();
valueOperations.set(ONLINE_TOKEN_POOL_KEY + userId, model, expTime, TimeUnit.MINUTES);
return;
}
Object listObj = null;
Set<OnlineUserModel> models = null;
if (ObjectUtils.isEmpty(listObj = CacheUtils.get(ONLINE_TOKEN_POOL_KEY, userId))) {
models = new LinkedHashSet<>();
} else {
models = (Set<OnlineUserModel>) listObj;
}
models.add(buildModel(token));
CacheUtils.put(ONLINE_TOKEN_POOL_KEY, userId, models, null, null);
Long time = expTime * 60;
Double v = time * 0.6;
CacheUtils.put(ONLINE_TOKEN_POOL_KEY, userId, model, time.intValue(), v.intValue());
CacheUtils.flush(ONLINE_TOKEN_POOL_KEY);
}
@ -121,16 +91,18 @@ public class TokenCacheUtils {
return service.multiLoginType();
}
public static Set<OnlineUserModel> onlineUserTokens(Long userId) {
public static OnlineUserModel onlineUserToken(Long userId) {
if (useRedis()) {
RedisTemplate redisTemplate = (RedisTemplate) CommonBeanFactory.getBean("redisTemplate");
SetOperations setOperations = redisTemplate.opsForSet();
Set tokens = setOperations.members(ONLINE_TOKEN_POOL_KEY + userId);
return tokens;
ValueOperations valueOperations = cacheHandler();
Object obj = valueOperations.get(ONLINE_TOKEN_POOL_KEY + userId);
if (ObjectUtils.isNotEmpty(obj)) return (OnlineUserModel) obj;
return null;
}
Object o = CacheUtils.get(ONLINE_TOKEN_POOL_KEY, userId);
if (ObjectUtils.isNotEmpty(o))
return (Set<OnlineUserModel>) o;
if (ObjectUtils.isNotEmpty(o)) {
OnlineUserModel userModel = gson.fromJson(gson.toJson(o), OnlineUserModel.class);
return userModel;
}
return null;
}