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

View File

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