perf: 优化缓存处理

1. 取消普通缓存使用分布式锁(没必要)
2. 优化安全缓存代码,减少体量级
3. 重新定义缓存存储结构
4. 取消Ehcache
本地缓存(后期自己需要可以用Guava)
pull/10/head
Parker 4 years ago
parent 0179041a6e
commit 6702de0e8c

@ -19,10 +19,50 @@ public final class RedisConstants {
public static final String PREFIX_MENU_CODE = "kv#{}:menu:code:";
/** 参数编号 */
public static final String PREFIX_OPTIONS_CODE = "kv#{}:options:code";
public static final String PREFIX_OPTIONS_CODE = "hash#{}:options";
/** 用户搜索记录 */
public static final String PREFIX_HIS_USERNAME = "zset#{}:his:username:";
/** 用户ID */
public static final String PREFIX_USER_ID = "kv#{}:user_id:";
/** 用户ID 和 角色 */
public static final String PREFIX_USER_ID_AND_ROLES = "kv#{}:user_id:roles:";
/** 用户ID 和 默认角色 */
public static final String PREFIX_USER_ID_DEF_ROLE = "kv#{}:user_id:def_role_id:";
/** 用户ID 和 组织 */
public static final String PREFIX_USER_ID_ORGS = "kv#{}:user_id:orgs:";
/** 用户ID 和 默认组织 */
public static final String PREFIX_USER_ID_DEF_ORG = "kv#{}:user_id:def_org:";
/** 用户ID 和 权限 */
public static final String PREFIX_USER_ID_PERMISSIONS = "kv#{}:user_id:permissions:";
/** 用户ID 和 菜单 */
public static final String PREFIX_USER_ID_MENUS = "kv#{}:user_id:menus:";
/** 用户名 */
public static final String PREFIX_USERNAME = "kv#{}:username:";
/** 票据 */
public static final String PREFIX_TICKET = "set#{}:ticket:";
/** 账号失败次数 */
public static final String PREFIX_ACCOUNT_SLIP_COUNT = "kv#{}:account:slip:count:";
/** 账号失败锁定KEY */
public static final String PREFIX_ACCOUNT_SLIP_LOCK = "kv#{}:account:slip:lock:";
private RedisConstants(){}
}

@ -1,72 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.autoconfigure.conf;
import lombok.extern.slf4j.Slf4j;
import org.opsli.core.cache.pushsub.receiver.RedisPushSubReceiver;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
/**
*
*
* @author Parker
* @date 2020-09-15
**/
@Slf4j
@Configuration
@ConditionalOnProperty(name = "spring.redis.pushsub.enable", havingValue = "true")
public class RedisMessageListenerConfig {
/**
* redis
* redis
*
*/
@Bean
public RedisMessageListenerContainer container(LettuceConnectionFactory lettuceConnectionFactory) {
RedisPushSubReceiver receiver = new RedisPushSubReceiver();
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(lettuceConnectionFactory);
//订阅了的通道
container.addMessageListener(listenerAdapter(new RedisPushSubReceiver()), new PatternTopic(receiver.getListenerChannel()));
return container;
}
/**
*
*
*
*/
@Bean
public MessageListenerAdapter listenerAdapter(RedisPushSubReceiver baseReceiver) {
//这个地方 是给messageListenerAdapter 传入一个消息接受的处理器利用反射的方法调用“receiveMessage”
//也有好几个重载方法,这边默认调用处理器的方法 叫handleMessage 可以自己到源码里面看
//receiveMessage就是对应消费者那边的消费方法吗,而Receiver是自己弄的一个消费者类
return new MessageListenerAdapter(baseReceiver, "receiveMessage");
}
}

@ -32,7 +32,7 @@ import org.springframework.stereotype.Component;
@EqualsAndHashCode(callSuper = false)
public class CacheProperties {
public static final String PROP_PREFIX = "spring.cache-conf";
public static final String PROP_PREFIX = "spring.cache";
/** 缓存前缀 */
private String prefix;

@ -37,30 +37,24 @@ import org.opsli.api.base.warpper.ApiWrapper;
import org.opsli.api.wrapper.system.user.UserModel;
import org.opsli.common.annotation.RequiresPermissionsCus;
import org.opsli.common.annotation.hotdata.EnableHotData;
import org.opsli.common.constants.CacheConstants;
import org.opsli.common.constants.TreeConstants;
import org.opsli.common.enums.ExcelOperate;
import org.opsli.common.exception.ServiceException;
import org.opsli.common.exception.TokenException;
import org.opsli.common.msg.CommonMsg;
import org.opsli.common.utils.OutputStreamUtil;
import org.opsli.common.utils.WrapperUtil;
import org.opsli.core.autoconfigure.properties.GlobalProperties;
import org.opsli.core.base.entity.BaseEntity;
import org.opsli.core.base.entity.HasChildren;
import org.opsli.core.base.service.interfaces.CrudServiceInterface;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.msg.CoreMsg;
import org.opsli.core.msg.TokenMsg;
import org.opsli.core.security.shiro.realm.JwtRealm;
import org.opsli.core.utils.DistributedLockUtil;
import org.opsli.core.utils.ExcelUtil;
import org.opsli.core.utils.UserUtil;
import org.opsli.plugins.excel.exception.ExcelPluginException;
import org.opsli.plugins.excel.listener.BatchExcelListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
@ -98,90 +92,6 @@ public abstract class BaseRestController <T extends BaseEntity, E extends ApiWra
@Autowired(required = false)
protected S IService;
/**
*
* id
*
* @param id id
* @return E
*/
@ModelAttribute
public E get(@RequestParam(required=false) String id) {
E model;
if (StringUtils.isNotBlank(id)){
// 如果开启缓存 先从缓存读
if(hotDataFlag){
// 缓存Key
String cacheKey = CacheConstants.HOT_DATA_PREFIX +":"+ id;
model = WrapperUtil.transformInstance(
CacheUtil.getTimed(entityClazz, cacheKey)
, modelClazz);
if(model != null){
return model;
}
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(!hasNilFlag) {
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
throw new ServiceException(CoreMsg.CACHE_PUNCTURE_EXCEPTION);
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
model = WrapperUtil.transformInstance(
CacheUtil.getTimed(entityClazz, cacheKey)
, modelClazz);
if(model != null){
return model;
}
// 如果缓存没读到 则去数据库读
model = WrapperUtil.transformInstance(IService.get(id), modelClazz);
// 获得数据后处理
if(model != null){
// 这里会 同步更新到本地Ehcache 和 Redis缓存
// 如果其他服务器缓存也丢失了 则 回去Redis拉取
CacheUtil.put(cacheKey, model);
return model;
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
if(model == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
}
}
}else {
// 如果没开启缓存 则直接查询数据库
model = WrapperUtil.transformInstance(IService.get(id), modelClazz);
if(model != null){
return model;
}
}
}
// 如果参数没传入ID 则创建一个空的对象
try {
// 创建泛型对象
model = this.createModel();
}catch (Exception e){
log.error(CommonMsg.EXCEPTION_CONTROLLER_MODEL.toString()+" : {}",e.getMessage());
throw new ServiceException(CommonMsg.EXCEPTION_CONTROLLER_MODEL);
}
return model;
}
/**
* Excel
* @param request request

@ -15,46 +15,21 @@
*/
package org.opsli.core.cache;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.opsli.common.constants.CacheConstants;
import org.opsli.common.enums.CacheType;
import org.opsli.core.autoconfigure.properties.CacheProperties;
import org.opsli.core.msg.CoreMsg;
import org.opsli.core.utils.ThrowExceptionUtil;
import org.opsli.plugins.cache.EhCachePlugin;
import org.opsli.plugins.redis.RedisPlugin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
/**
*
*
* keyRedis
* 使expire()idletime
*
* bigkey()
* string10KBhashlistsetzset5000
*
* @author Parker
* @date 2020-09-16 16:20
*/
@ -63,20 +38,6 @@ import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
@Component
public class CacheUtil {
/** 热点数据缓存时间 秒 (6小时)*/
private static int TTL_HOT_DATA_TIME = 21600;
/** 空缓存时间 秒 */
private final static int TTL_NIL_DATA_TIME = 300;
/** Redis插件 */
private static RedisPlugin redisPlugin;
/** EhCache插件 */
private static EhCachePlugin ehCachePlugin;
/** Json key */
public static final String JSON_KEY = "data";
/** 空状态 key 前缀 */
private static final String NIL_FLAG_PREFIX = "nil";
/** 空状态 生效阈值 */
private final static long NIL_FLAG_THRESHOLD = 3;
/** 热点数据前缀 */
private static String PREFIX_NAME;
@ -84,661 +45,7 @@ public class CacheUtil {
/** 增加初始状态开关 防止异常使用 */
private static boolean IS_INIT;
static {
try {
// 读取配置信息
CacheUtil.readPropertyXML();
}catch (Exception ignored){}
}
/***
*
* @return String
*/
public static String getPrefixName() {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
return PREFIX_NAME;
}
// ========================= GET =========================
/**
*
* @param key
* @return Object
*/
public static Object getTimed(final String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(key, false, false);
}
/**
*
* @param key
* @param isSaveLocal
* @return Object
*/
public static Object getTimed(final String key, final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(key, false, isSaveLocal);
}
/**
*
* @param vClass Class
* @param key
* @return <V>
*/
public static <V> V getTimed(final Class<V> vClass, final String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(vClass, key, false, false);
}
/**
*
* @param vClass Class
* @param key
* @param isSaveLocal
* @return <V>
*/
public static <V> V getTimed(final Class<V> vClass, final String key,
final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(vClass, key, false, isSaveLocal);
}
/**
*
* @param key
* @return Object
*/
public static Object getEden(final String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(key, true, false);
}
/**
*
* @param key
* @param isSaveLocal
* @return Object
*/
public static Object getEden(final String key, final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(key, true, isSaveLocal);
}
/**
*
* @param vClass Class
* @param key
* @return <V>
*/
public static <V> V getEden(final String key, final Class<V> vClass){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(vClass, key, true, false);
}
/**
*
* @param vClass Class
* @param key
* @param isSaveLocal
* @return <V>
*/
public static <V> V getEden(final String key, final boolean isSaveLocal,
final Class<V> vClass){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 转换数据泛型
return CacheUtil.get(vClass, key, true, isSaveLocal);
}
/**
*
* @param vClass Class
* @param key
* @param isEden
* @param isSaveLocal
* @return <V>
*/
private static <V> V get(final Class<V> vClass, final String key, final boolean isEden,
final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 获得缓存数据
Object cacheObj = CacheUtil.get(key, isEden, isSaveLocal);
// 转换数据泛型
return Convert.convert(vClass, cacheObj);
}
/**
*
* @param key
* @param isEden
* @param isSaveLocal
* @return Object
*/
private static Object get(final String key, final boolean isEden, final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 缓存 Key
String cacheKey = CacheUtil.handleUsualKey(key, isEden);
// 获得缓存Json
JSONObject cacheJson;
// 判读是否需要 先从本地缓存获取
if(isSaveLocal){
// 获得缓存Json
cacheJson = ehCachePlugin.get(CacheConstants.EHCACHE_SPACE,
cacheKey, JSONObject.class);
if(cacheJson != null){
return cacheJson.get(JSON_KEY);
}
}
// 如果本地缓存找不到该缓存 则去远端缓存拉去缓存
cacheJson = (JSONObject) redisPlugin.get(cacheKey);
if(cacheJson != null){
// 判读是否需要 存入本地EhCache
if(isSaveLocal){
//存入EhCache
ehCachePlugin.put(CacheConstants.EHCACHE_SPACE,
cacheKey, cacheJson);
}
}
return cacheJson != null ? cacheJson.get(JSON_KEY) : null;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return null;
}
/**
* Hash
* @param vClass Class
* @param key
* @param field
* @return <V>
*/
public static <V> V getHash(final Class<V> vClass, final String key, final String field){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 获得缓存数据
Object cacheObj = CacheUtil.getHash(key, field, false);
// 转换数据泛型
return Convert.convert(vClass, cacheObj);
}
/**
* Hash
* @param key
* @param field
* @param isSaveLocal
* @param vClass Class
* @return <V>
*/
public static <V> V getHash(final Class<V> vClass, final String key,
final String field, final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 获得缓存数据
Object cacheObj = CacheUtil.getHash(key, field, isSaveLocal);
// 转换数据泛型
return Convert.convert(vClass, cacheObj);
}
/**
* Hash
* @param key
* @param field
* @return Object
*/
public static Object getHash(final String key, final String field){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
return CacheUtil.getHash(key, field, false);
}
/**
* Hash
* @param key
* @param field
* @param isSaveLocal
* @return Object
*/
public static Object getHash(final String key, final String field,
final boolean isSaveLocal){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 缓存 Key
String cacheKey = CacheUtil.handleKey(CacheType.EDEN_HASH, key);
// 获得缓存Json
JSONObject cacheJson;
// 判读是否需要 先从本地缓存获取
if(isSaveLocal){
// 获得缓存Json
cacheJson = ehCachePlugin.get(CacheConstants.EHCACHE_SPACE,
cacheKey +":"+ field, JSONObject.class);
if(cacheJson != null){
return cacheJson.get(JSON_KEY);
}
}
// 如果本地缓存找不到该缓存 则去远端缓存拉去缓存
cacheJson = (JSONObject) redisPlugin.hGet(cacheKey, field);
if(cacheJson != null){
// 判读是否需要 存入本地EhCache
if(isSaveLocal){
//存入EhCache
ehCachePlugin.put(CacheConstants.EHCACHE_SPACE,
cacheKey + ":" + field, cacheJson);
}
}
return cacheJson != null ? cacheJson.get(JSON_KEY) : null;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return null;
}
/**
* Hash
* @param key
* @return Object
*/
public static Map<String, Object> getHashAll(final String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 缓存 Key
String cacheKey = CacheUtil.handleKey(CacheType.EDEN_HASH, key);
Map<String, Object> retMap = Maps.newHashMap();
// 如果本地缓存找不到该缓存 则去远端缓存拉去缓存
Map<Object, Object> allCache = redisPlugin.hGetAll(cacheKey);
if(CollUtil.isEmpty(allCache)){
return retMap;
}
for (Map.Entry<Object, Object> entry : allCache.entrySet()) {
// 赋值
JSONObject jsonObject = (JSONObject) entry.getValue();
if (jsonObject == null) {
continue;
}
Object data = jsonObject.get(CacheUtil.JSON_KEY);
if (data == null) {
continue;
}
retMap.put(Convert.toStr(entry.getKey()), data);
}
return retMap;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return null;
}
// ========================= PUT =========================
/**
*
* @param key
* @param value
* @return boolean
*/
public static boolean put(final String key, final Object value) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
return CacheUtil.put(key, value, false);
}
/**
*
* @param key
* @param value
* @param isEden
* @return boolean
*/
public static boolean put(final String key, final Object value, final boolean isEden) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 自动处理 key
// 则统一转换为 JSONObject
JSONObject cacheJson = new JSONObject();
cacheJson.put(JSON_KEY, value);
// 缓存 Key
String cacheKey = CacheUtil.handleUsualKey(key, isEden);
// 判断是否为永久存储
if(isEden) {
// 存入Redis
return redisPlugin.put(cacheKey, cacheJson);
}else{
// 随机缓存失效时间 防止缓存雪崩
// 范围在当前时效的 1.2 - 2倍
// 生成随机失效时间
int timeout = RandomUtil.randomInt(
Convert.toInt(TTL_HOT_DATA_TIME * 1.2),
Convert.toInt(TTL_HOT_DATA_TIME * 2)
);
// 存入Redis
return redisPlugin.put(cacheKey, cacheJson, timeout);
}
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
/**
* Hash
* @param key
* @param field
* @param value
* @return boolean
*/
public static boolean putHash(final String key, final String field, final Object value) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 处理 key
String cacheKey = CacheUtil.handleKey(CacheType.EDEN_HASH, key);
// 则统一转换为 JSONObject
JSONObject cacheJson = new JSONObject();
cacheJson.put(JSON_KEY, value);
// 存入Redis
return redisPlugin.hPut(cacheKey, field, cacheJson);
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
// ========================= DEL =========================
/**
*
* @param key
* @return boolean
*/
public static boolean del(final String key) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 计数器
int count = 0;
Object timed = CacheUtil.getTimed(key);
Object eden = CacheUtil.getEden(key);
// 删除key 集合
List<String> cacheKeys = Lists.newArrayList();
if(timed != null){
count+=2;
// 处理 key - 时控数据
cacheKeys.add(
CacheUtil.handleKey(CacheType.TIMED, key)
);
}
if(eden != null){
count+=2;
// 处理 key - 永久数据
cacheKeys.add(
CacheUtil.handleKey(CacheType.EDEN, key));
}
// 循环删除缓存数据
for (String cacheKey : cacheKeys) {
// 删除 EhCache
boolean ehcacheRet = ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE, cacheKey);
if(ehcacheRet){
count--;
}
// 删除 Redis
boolean redisRet = redisPlugin.del(cacheKey);
if(redisRet){
count--;
}
}
return count == 0;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
/**
* Hash
* @param key
* @param field
* @return boolean
*/
public static boolean delHash(final String key, final String field) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
try {
// 计数器
int count = 2;
// 自动处理 key
String cacheKey = CacheUtil.handleKey(CacheType.EDEN_HASH, key);
// 删除 EhCache
boolean ehcacheRet = ehCachePlugin.delete(CacheConstants.EHCACHE_SPACE,cacheKey +":"+ field);
if(ehcacheRet){
count--;
}
// 删除 Redis
Long hDeleteLong = redisPlugin.hDelete(cacheKey, field);
if(hDeleteLong != null && hDeleteLong > 0){
count--;
}
return count == 0;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
// ====================================================================
/**
* 5
* 穿
*
* @param key
* @return boolean
*/
public static boolean putNilFlag(String key) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 处理缓存 key
String cacheKey = CacheUtil.handleKey(NIL_FLAG_PREFIX + ":" + key);
try {
// 存入Redis
Long increment = redisPlugin.increment(cacheKey);
// 设置失效时间
redisPlugin.expire(cacheKey, TTL_NIL_DATA_TIME);
return increment != null;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
/**
*
* 穿
*
* @param key
* @return boolean
*/
public static boolean delNilFlag(String key) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 处理缓存 key
String cacheKey = CacheUtil.handleKey(NIL_FLAG_PREFIX + ":" + key);
try {
// 删除Redis
return redisPlugin.del(cacheKey);
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
/**
* 5
* 穿
*
* @param key
* @return boolean
*/
public static boolean hasNilFlag(String key) {
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 处理缓存 key
String cacheKey = CacheUtil.handleKey(NIL_FLAG_PREFIX + ":" + key);
try {
Object nilObj = redisPlugin.get(cacheKey);
if(nilObj == null){
return false;
}
Long nilNum = Convert.toLong(nilObj, 0L);
return NIL_FLAG_THRESHOLD < nilNum;
}catch (Exception e){
log.error(e.getMessage(),e);
}
return false;
}
// ====================================================================
/**
* key
* @param key Key
* @return String
*/
public static String handleKey(String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
return CacheUtil.handleKey(CacheType.TIMED, key);
}
/**
* key
* @param cacheType
* @param key Key
* @return String
*/
public static String handleKey(CacheType cacheType, String key){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
return PREFIX_NAME + cacheType.getName() + ":" +
key;
}
/**
* Key
@ -754,74 +61,12 @@ public class CacheUtil {
}
/**
* key
* @param key Key
* @param isEden
* @return String
*/
private static String handleUsualKey(String key, boolean isEden){
if(isEden){
return CacheUtil.handleKey(CacheType.EDEN, key);
}
return CacheUtil.handleKey(CacheType.TIMED, key);
}
/**
*
*/
private static void readPropertyXML() throws IOException {
// 有坑 读 xml
ClassPathResource resource = new ClassPathResource("config/ehcache-opsli.xml");
Document document = XmlUtil.readXML(resource.getInputStream());
NodeList nodeList = document.getElementsByTagName("cache");
if(nodeList != null){
for (int i = 0; i < nodeList.getLength(); i++) {
Node item = nodeList.item(i);
NamedNodeMap attributes = item.getAttributes();
if(attributes == null){
continue;
}
Node alias = attributes.getNamedItem("alias");
if("hotData".equals(alias.getNodeValue())){
NodeList childNodes = item.getChildNodes();
if(childNodes != null){
for (int j = 0; j < childNodes.getLength(); j++) {
if("expiry".equals(childNodes.item(j).getNodeName())){
NodeList expiryNodes = childNodes.item(j).getChildNodes();
if(expiryNodes != null){
for (int k = 0; k < expiryNodes.getLength(); k++) {
if("ttl".equals(expiryNodes.item(k).getNodeName())){
Node ttlNode = expiryNodes.item(k);
Node ttlValue = ttlNode.getFirstChild();
// 默认 60000秒 6小时
TTL_HOT_DATA_TIME = Convert.toInt(ttlValue.getNodeValue(), 21600);
break;
}
}
}
break;
}
}
}
break;
}
}
}
}
/**
*
*/
@Autowired
public void init(CacheProperties cacheProperties,
RedisPlugin redisPlugin,
EhCachePlugin ehCachePlugin){
CacheUtil.PREFIX_NAME = Convert.toStr(cacheProperties.getPrefix(), "opsli") + ":";
CacheUtil.redisPlugin = redisPlugin;
CacheUtil.ehCachePlugin = ehCachePlugin;
public void init(CacheProperties cacheProperties){
CacheUtil.PREFIX_NAME = Convert.toStr(cacheProperties.getPrefix(), "opsli");
IS_INIT = true;
}

@ -1,8 +1,24 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.cache;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.util.concurrent.Striped;
@ -144,10 +160,12 @@ public final class SecurityCache {
throw new RuntimeException("入参[redisTemplate,key,val]必填");
}
String cacheKey = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_KV);
// 判断是否为永久存储
if(isEden) {
redisTemplate.opsForValue()
.set("kv#" + key, val);
.set(cacheKey, val);
}else{
// 随机缓存失效时间 防止缓存雪崩
// 范围在当前时效的 1.2 - 2倍
@ -159,7 +177,12 @@ public final class SecurityCache {
);
redisTemplate.opsForValue()
.set(CACHE_PREFIX_KV + key, val, timeout, TimeUnit.SECONDS);
.set(
cacheKey,
val,
timeout,
TimeUnit.SECONDS
);
}
// 清除本地记录
@ -356,8 +379,10 @@ public final class SecurityCache {
throw new RuntimeException("入参[redisTemplate,key,cacheMap]必填");
}
String cacheKeyByHash = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_HASH);
redisTemplate.opsForHash()
.putAll(CACHE_PREFIX_HASH + key, cacheMap);
.putAll(cacheKeyByHash, cacheMap);
// 清除本地记录
LFU_NULL_CACHE.invalidate(key);
@ -378,8 +403,10 @@ public final class SecurityCache {
throw new RuntimeException("入参[redisTemplate,key,field,val]必填");
}
String cacheKeyByHash = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_HASH);
redisTemplate.opsForHash()
.put(CACHE_PREFIX_HASH + key, field, val);
.put(cacheKeyByHash, field, val);
final String tempKey = key + "_" + field;
// 清除本地记录
@ -431,8 +458,13 @@ public final class SecurityCache {
// 清除本地记录
LFU_NULL_CACHE.invalidate(key);
String cacheKeyByKv = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_KV);
String cacheKeyByHash = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_HASH);
// 清除缓存
redisTemplate.delete(key);
redisTemplate.delete(cacheKeyByKv);
redisTemplate.delete(cacheKeyByHash);
return true;
}
@ -455,8 +487,10 @@ public final class SecurityCache {
return null;
}
String cacheKey = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_KV);
// 从 缓存回调查询数据
cache = redisTemplate.opsForValue().get(CACHE_PREFIX_KV + key);
cache = redisTemplate.opsForValue().get(cacheKey);
}catch (Exception e){
log.error(e.getMessage(), e);
}
@ -481,8 +515,10 @@ public final class SecurityCache {
return null;
}
String cacheKeyByHash = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_HASH);
// 从 缓存回调查询数据
cache = redisTemplate.opsForHash().get(CACHE_PREFIX_HASH + key, field);
cache = redisTemplate.opsForHash().get(cacheKeyByHash, field);
}catch (Exception e){
log.error(e.getMessage(), e);
}
@ -508,8 +544,10 @@ public final class SecurityCache {
return null;
}
String cacheKeyByHash = StrUtil.addPrefixIfNot(key, CACHE_PREFIX_HASH);
// 从 缓存回调查询数据
Map<Object, Object> entries = redisTemplate.opsForHash().entries(CACHE_PREFIX_HASH + key);
Map<Object, Object> entries = redisTemplate.opsForHash().entries(cacheKeyByHash);
// 如果补偿器不为空 则进行补偿判断
if(null != callbackSourceCount){

@ -1,299 +0,0 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.filters.aspect;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.opsli.api.base.warpper.ApiWrapper;
import org.opsli.common.annotation.hotdata.EnableHotData;
import org.opsli.common.annotation.hotdata.HotDataDel;
import org.opsli.common.annotation.hotdata.HotDataPut;
import org.opsli.common.constants.CacheConstants;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.cache.pushsub.entity.CacheDataEntity;
import org.opsli.core.cache.pushsub.enums.CacheHandleType;
import org.opsli.core.cache.pushsub.msgs.CacheDataMsgFactory;
import org.opsli.plugins.redis.RedisPlugin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import static org.opsli.common.constants.OrderConstants.HOT_DATA_ORDER;
/**
*
*
* @author parker
* @date 2020-09-16
*/
@Slf4j
@Order(HOT_DATA_ORDER)
@Aspect
@Component
public class CacheDataAop {
@Autowired
private RedisPlugin redisPlugin;
@Pointcut("@annotation(org.opsli.common.annotation.hotdata.HotDataPut)")
public void hotDataPut() {
}
@Pointcut("@annotation(org.opsli.common.annotation.hotdata.HotDataDel)")
public void hotDataDel() {
}
/**
*
* @param point point
* @return Object
* @throws Throwable
*/
@Around("hotDataPut()")
public Object hotDataPutProcess(ProceedingJoinPoint point) throws Throwable {
Object[] args = point.getArgs();
Object returnValue = point.proceed(args);
// 判断 方法上是否使用 EnableHotData注解 如果没有表示开启热数据 则直接跳过
Annotation annotation = point.getTarget().getClass().getAnnotation(EnableHotData.class);
if(annotation == null){
return returnValue;
}
List<CacheDataEntity> cacheDataEntityList = this.putHandlerData(point, returnValue);
// 非法判断
if(CollUtil.isEmpty(cacheDataEntityList)){
return returnValue;
}
for (CacheDataEntity cacheDataEntity : cacheDataEntityList) {
// 更新缓存数据
// 热点数据
boolean putRet = CacheUtil.put(CacheConstants.HOT_DATA_PREFIX +":"+ cacheDataEntity.getKey(),
returnValue);
if(putRet){
// 广播缓存数据 - 通知其他服务器同步数据
redisPlugin.sendMessage(
CacheDataMsgFactory.createMsg(
cacheDataEntity, returnValue, CacheHandleType.UPDATE)
);
}
}
return returnValue;
}
/**
*
* @param point point
* @return Object
* @throws Throwable
*/
@Around("hotDataDel()")
public Object hotDataDelProcess(ProceedingJoinPoint point) throws Throwable {
Object[] args= point.getArgs();
Object returnValue = point.proceed(args);
// 判断 方法上是否使用 EnableHotData注解 如果没有表示开启热数据 则直接跳过
Annotation annotation = point.getTarget().getClass().getAnnotation(EnableHotData.class);
if(annotation == null){
return returnValue;
}
// 删除状态判断
try {
Boolean ret = (Boolean) returnValue;
if(ret == null || !ret){
return returnValue;
}
}catch (Exception e){
log.error(e.getMessage(),e);
return returnValue;
}
List<CacheDataEntity> cacheDataEntityList = this.delHandlerData(point, args);
// 非法判断
if(CollUtil.isEmpty(cacheDataEntityList)){
return returnValue;
}
for (CacheDataEntity cacheDataEntity : cacheDataEntityList) {
// 更新缓存数据 - 删除缓存
boolean delRet = CacheUtil.del(CacheConstants.HOT_DATA_PREFIX +":"+ cacheDataEntity.getKey());
if(delRet){
// 广播缓存数据 - 通知其他服务器同步数据
redisPlugin.sendMessage(
CacheDataMsgFactory.createMsg(
cacheDataEntity, null, CacheHandleType.DELETE)
);
}
}
return returnValue;
}
// ===========================================================================
/***
* PUT
* @param point point
*/
private List<CacheDataEntity> putHandlerData(ProceedingJoinPoint point, Object returnValue){
// 这里 只对 继承了 ApiWrapper 的类做处理
if(!(returnValue instanceof ApiWrapper)){
return null;
}
// 消息集合 后续可能会考虑 多消息存储
List<CacheDataEntity> cacheDataEntities = Lists.newArrayListWithCapacity(1);
// 报错不处理
try {
// 获得方法
Method objMethod = this.getMethod(point);
if(objMethod == null) {
return null;
}
// 获取注解参数
HotDataPut aCache = objMethod.getAnnotation(HotDataPut.class);
if(aCache != null){
// 这里 只对 继承了 BaseEntity 的类做处理
ApiWrapper apiWrapper = (ApiWrapper) returnValue;
CacheDataEntity ret = new CacheDataEntity(apiWrapper.getId());
// 存放数据
this.putCacheData(cacheDataEntities, ret);
return cacheDataEntities;
}
}catch (Exception e){
log.error(e.getMessage(),e);
}
return null;
}
/***
* DEL
* @param point point
*/
private List<CacheDataEntity> delHandlerData(ProceedingJoinPoint point, Object[] args){
if(args == null || args.length == 0){
return null;
}
// 消息集合
List<CacheDataEntity> cacheDataEntities = Lists.newArrayListWithCapacity(args.length);
// 报错不处理
try {
// 获得方法
Method objMethod = this.getMethod(point);
if(objMethod == null) {
return null;
}
// 获取注解参数
HotDataDel aCache= objMethod.getAnnotation(HotDataDel.class);
if(aCache != null){
List<String> keyList = null;
// 处理数据
for (Object arg : args) {
if (arg instanceof ApiWrapper) {
// key 存储ID
ApiWrapper apiWrapper = Convert.convert(ApiWrapper.class, arg);
keyList = Convert.toList(String.class, apiWrapper.getId());
} else if (arg instanceof Collection) {
try {
keyList = Lists.newArrayList();
List<ApiWrapper> baseEntityList = Convert.toList(ApiWrapper.class, arg);
for (ApiWrapper baseEntity : baseEntityList) {
keyList.add(baseEntity.getId());
}
}catch (Exception e){
log.error(e.getMessage(),e);
}
}else {
keyList = Convert.toList(String.class, arg);
}
}
if(keyList != null && CollUtil.isNotEmpty(keyList)){
for (String key : keyList) {
CacheDataEntity ret = new CacheDataEntity(key);
// 存放数据
this.putCacheData(cacheDataEntities, ret);
}
}
return cacheDataEntities;
}
}catch (Exception e){
log.error(e.getMessage(),e);
}
return null;
}
// =====================
/**
*
* @param point point
* @return Method
*/
private Method getMethod(ProceedingJoinPoint point){
Method m = null;
try {
String methodName= point.getSignature().getName();
Class<?> classTarget= point.getTarget().getClass();
Class<?>[] par=((MethodSignature) point.getSignature()).getParameterTypes();
m = classTarget.getMethod(methodName, par);
}catch (Exception ignored){}
return m;
}
/**
*
* @param cacheDataList
* @param cacheData
*/
private void putCacheData(List<CacheDataEntity> cacheDataList, CacheDataEntity cacheData){
// 非法判断
if(cacheDataList == null){
return;
}
cacheDataList.add(cacheData);
}
}

@ -98,8 +98,11 @@ public class CaptchaUtil {
// 生成验证码
Captcha captcha = captchaStrategy.createCaptcha();
// 缓存Key
String cacheKey = CacheUtil.formatKey(PREFIX + uuid);
// 保存至缓存
boolean ret = redisPlugin.put(CacheUtil.getPrefixName() + PREFIX + uuid, captcha.text(), TIME_OUT);
boolean ret = redisPlugin.put(cacheKey, captcha.text(), TIME_OUT);
if(ret){
// 输出
captcha.out(out);
@ -127,8 +130,11 @@ public class CaptchaUtil {
throw new TokenException(TokenMsg.EXCEPTION_CAPTCHA_CODE_NULL);
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(PREFIX + uuid);
// 验证码
String codeTemp = (String) redisPlugin.get(CacheUtil.getPrefixName() + PREFIX + uuid);
String codeTemp = (String) redisPlugin.get(cacheKey);
if (StringUtils.isEmpty(codeTemp)) {
throw new TokenException(TokenMsg.EXCEPTION_CAPTCHA_NULL);
}
@ -156,8 +162,11 @@ public class CaptchaUtil {
return false;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(PREFIX + uuid);
//删除验证码
return redisPlugin.del(CacheUtil.getPrefixName() + PREFIX + uuid);
return redisPlugin.del(cacheKey);
}
// ======================

@ -60,7 +60,9 @@ public class DistributedLockUtil {
boolean isLock = true;
// 分布式上锁
if(REDISSON_LOCK != null){
isLock = REDISSON_LOCK.tryLock(CacheUtil.getPrefixName() + lockName, LEASE_TIME);
// 缓存Key
String cacheKey = CacheUtil.formatKey(lockName);
isLock = REDISSON_LOCK.tryLock(cacheKey, LEASE_TIME);
}
return isLock;
}
@ -76,7 +78,9 @@ public class DistributedLockUtil {
// 释放锁
if(REDISSON_LOCK != null){
REDISSON_LOCK.unlockByThread(CacheUtil.getPrefixName() + lockName);
// 缓存Key
String cacheKey = CacheUtil.formatKey(lockName);
REDISSON_LOCK.unlockByThread(cacheKey);
}
}

@ -45,9 +45,6 @@ import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
@Lazy(false)
public class MenuUtil {
/** 前缀 */
public static final String PREFIX_CODE = "menu:code:";
/** 菜单 Api */
private static MenuApi menuApi;

@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.opsli.api.base.result.ResultVo;
import org.opsli.api.wrapper.system.user.UserModel;
import org.opsli.common.constants.RedisConstants;
import org.opsli.common.constants.SignConstants;
import org.opsli.common.constants.TokenConstants;
import org.opsli.common.constants.TokenTypeConstants;
@ -64,12 +65,6 @@ public class UserTokenUtil {
/** token 缓存名 */
public static final String TOKEN_NAME = TokenConstants.ACCESS_TOKEN;
/** 缓存前缀 */
private static final String TICKET_PREFIX = "ticket:";
/** 账号失败次数 */
public static final String ACCOUNT_SLIP_COUNT_PREFIX = "account:slip:count:";
/** 账号失败锁定KEY */
public static final String ACCOUNT_SLIP_LOCK_PREFIX = "account:slip:lock:";
/** 限制登录数量 -1 为无限大 */
public static final int ACCOUNT_LIMIT_INFINITE = -1;
/** 登录配置信息 */
@ -100,8 +95,8 @@ public class UserTokenUtil {
// 如果当前登录开启 数量限制
if(LOGIN_PROPERTIES.getLimitCount() > ACCOUNT_LIMIT_INFINITE){
// 当前用户已存在 Token数量
Long ticketLen = redisPlugin.sSize(CacheUtil.getPrefixName() +
TICKET_PREFIX + user.getUsername());
Long ticketLen = redisPlugin.sSize(
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + user.getUsername()));
if(ticketLen !=null && ticketLen >= LOGIN_PROPERTIES.getLimitCount()){
// 如果是拒绝后者 则直接抛出异常
if(LoginLimitRefuse.AFTER == LOGIN_PROPERTIES.getLimitRefuse()){
@ -110,7 +105,9 @@ public class UserTokenUtil {
}
// 如果是拒绝前者 则弹出前者
else {
redisPlugin.sPop(CacheUtil.getPrefixName() + TICKET_PREFIX + user.getUsername());
redisPlugin.sPop(
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + user.getUsername())
);
}
}
}
@ -135,11 +132,13 @@ public class UserTokenUtil {
// 在redis存一份 token 是为了防止 人为造假
// 保存用户token
Long saveLong = redisPlugin.sPut(
CacheUtil.getPrefixName() + TICKET_PREFIX + user.getUsername(), signToken);
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + user.getUsername()),
signToken
);
if(saveLong != null && saveLong > 0){
// 设置该用户全部token失效时间 如果这时又有新设备登录 则续命
redisPlugin.expire(
CacheUtil.getPrefixName() + TICKET_PREFIX + user.getUsername(),
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + user.getUsername()),
JwtUtil.EXPIRE_MILLISECOND, TimeUnit.MILLISECONDS);
TokenRet tokenRet = new TokenRet();
@ -249,11 +248,13 @@ public class UserTokenUtil {
if(user != null){
// 删除Token信息
redisPlugin.sRemove(
CacheUtil.getPrefixName() + TICKET_PREFIX + user.getUsername(), token);
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + user.getUsername()),
token);
// 如果缓存中 无该用户任何Token信息 则删除用户缓存
Long size = redisPlugin.sSize(
CacheUtil.getPrefixName() + TICKET_PREFIX + user.getUsername());
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + user.getUsername())
);
if(size == null || size == 0L) {
// 删除相关信息
UserUtil.refreshUser(user);
@ -296,7 +297,8 @@ public class UserTokenUtil {
String username = getUserNameByToken(token);
boolean hashKey = redisPlugin.sHashKey(
CacheUtil.getPrefixName() + TICKET_PREFIX + username, token);
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + username),
token);
if(!hashKey){
return false;
}
@ -305,7 +307,7 @@ public class UserTokenUtil {
if(BooleanUtil.isTrue(LOGIN_PROPERTIES.getReviveMode())){
// 设置该用户全部token失效时间 如果这时又有新设备登录 则续命
redisPlugin.expire(
CacheUtil.getPrefixName() + TICKET_PREFIX + username,
CacheUtil.formatKey(RedisConstants.PREFIX_TICKET + username),
JwtUtil.EXPIRE_MILLISECOND, TimeUnit.MILLISECONDS);
}
@ -329,7 +331,7 @@ public class UserTokenUtil {
// 判断账号是否临时锁定
Long loseTimeMillis = (Long) redisPlugin.get(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_LOCK_PREFIX + username);
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_LOCK + username));
if(loseTimeMillis != null){
Date currDate = DateUtil.date();
DateTime loseDate = DateUtil.date(loseTimeMillis);
@ -364,18 +366,19 @@ public class UserTokenUtil {
// 如果失败次数 超过阈值 则锁定账号
Long slipNum = redisPlugin.increment(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_COUNT_PREFIX + username);
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_COUNT + username));
if (slipNum != null){
// 设置失效时间为 5分钟
redisPlugin.expire(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_COUNT_PREFIX + username, LOGIN_PROPERTIES.getSlipLockSpeed());
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_COUNT + username),
LOGIN_PROPERTIES.getSlipLockSpeed());
// 如果确认 都失败 则存入临时缓存
if(slipNum >= LOGIN_PROPERTIES.getSlipCount()){
long currentTimeMillis = System.currentTimeMillis();
// 存入Redis
redisPlugin.put(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_LOCK_PREFIX + username,
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_LOCK + username),
currentTimeMillis, LOGIN_PROPERTIES.getSlipLockSpeed());
}
}
@ -396,7 +399,7 @@ public class UserTokenUtil {
long count = 0L;
Object obj = redisPlugin.get(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_COUNT_PREFIX + username);
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_COUNT + username));
if(obj != null){
try {
count = Convert.convert(Long.class, obj);
@ -418,10 +421,10 @@ public class UserTokenUtil {
// 删除失败次数记录
redisPlugin.del(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_COUNT_PREFIX + username);
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_COUNT + username));
// 删除失败次数记录
redisPlugin.del(
CacheUtil.getPrefixName() + ACCOUNT_SLIP_LOCK_PREFIX + username);
CacheUtil.formatKey(RedisConstants.PREFIX_ACCOUNT_SLIP_LOCK + username));
}

@ -29,16 +29,18 @@ import org.opsli.api.wrapper.system.menu.MenuModel;
import org.opsli.api.wrapper.system.role.RoleModel;
import org.opsli.api.wrapper.system.user.UserModel;
import org.opsli.api.wrapper.system.user.UserOrgRefModel;
import org.opsli.api.wrapper.system.user.UserOrgRefWebModel;
import org.opsli.common.constants.RedisConstants;
import org.opsli.common.exception.TokenException;
import org.opsli.core.api.TokenThreadLocal;
import org.opsli.core.autoconfigure.properties.GlobalProperties;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.cache.SecurityCache;
import org.opsli.core.msg.CoreMsg;
import org.opsli.core.msg.TokenMsg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@ -57,16 +59,6 @@ import static org.opsli.common.constants.OrderConstants.UTIL_ORDER;
@Lazy(false)
public class UserUtil {
/** 前缀 */
public static final String PREFIX_ID = "userId:";
public static final String PREFIX_ID_ROLES = "userId:roles:";
public static final String PREFIX_ID_DEF_ROLE = "userId:def_role:";
public static final String PREFIX_ID_ORGS = "userId:orgs:";
public static final String PREFIX_ID_DEF_ORG = "userId:def_org:";
public static final String PREFIX_ID_PERMISSIONS = "userId:permissions:";
public static final String PREFIX_ID_MENUS = "userId:menus:";
public static final String PREFIX_USERNAME = "username:";
/** 修改租户权限 */
private static final String PERMS_TENANT = "system_user_tenant";
@ -85,6 +77,8 @@ public class UserUtil {
/** 增加初始状态开关 防止异常使用 */
private static boolean IS_INIT;
private static RedisTemplate<String, Object> redisTemplate;
/**
*
* @return UserModel
@ -169,67 +163,24 @@ public class UserUtil {
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 缓存Key
String cacheKey = PREFIX_ID + userId;
// 先从缓存里拿
UserModel userModel = CacheUtil.getTimed(UserModel.class, cacheKey);
if (userModel != null){
// 如果不是递归触发 可进入一次
if(!isRecursion){
// 如果是 切换租户权限 则进行新一轮判断
// 注意不要陷入递归死循环 只保障循环一次
if (StringUtils.isNotBlank(userModel.getSwitchTenantUserId())){
userModel = getUser(userModel.getSwitchTenantUserId(), true);
}
}
return userModel;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return null;
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
userModel = CacheUtil.getTimed(UserModel.class, cacheKey);
if (userModel != null){
return userModel;
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID + userId);
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
UserModel userModelTemp = new UserModel();
userModelTemp.setId(userId);
// 设置为系统内部调用 否则 会拿到 空值
userModelTemp.setIzApi(true);
// 查询数据库
ResultVo<UserModel> resultVo = userApi.get(userModelTemp);
if(resultVo.isSuccess()){
userModel = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, userModel);
if(!resultVo.isSuccess()){
return null;
}
return resultVo.getData();
}, true);
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
if(userModel == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
return null;
}
UserModel userModel = Convert.convert(UserModel.class, cache);
// 如果不是递归触发 可进入一次
if(!isRecursion){
@ -255,56 +206,18 @@ public class UserUtil {
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 缓存Key
String cacheKey = PREFIX_USERNAME + userName;
// 先从缓存里拿
UserModel userModel = CacheUtil.getTimed(UserModel.class, cacheKey);
if (userModel != null){
return userModel;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return null;
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
userModel = CacheUtil.getTimed(UserModel.class, cacheKey);
if (userModel != null) {
return userModel;
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USERNAME + userName);
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<UserModel> resultVo = userApi.getUserByUsername(userName);
if (resultVo.isSuccess()) {
userModel = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, userModel);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return resultVo.getData();
}, true);
if(userModel == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
return null;
}
return userModel;
return Convert.convert(UserModel.class, cache);
}
/**
@ -325,59 +238,22 @@ public class UserUtil {
}
// 缓存Key
String cacheKey = PREFIX_ID_ROLES + userId;
List<String> roles;
// 先从缓存里拿
Object obj = CacheUtil.getTimed(cacheKey);
roles = Convert.toList(String.class, obj);
if(CollUtil.isNotEmpty(roles)){
return roles;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return ListUtil.empty();
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return ListUtil.empty();
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
obj = CacheUtil.getTimed(cacheKey);
roles = Convert.toList(String.class, obj);
if(CollUtil.isNotEmpty(roles)){
return roles;
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_AND_ROLES + userId);
final String finalUserId = userId;
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<List<String>> resultVo = userRoleRefApi.getRolesByUserId(userId);
if(resultVo.isSuccess()){
roles = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, roles);
ResultVo<List<String>> resultVo = userRoleRefApi.getRolesByUserId(finalUserId);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return resultVo.getData();
}, true);
if(CollUtil.isEmpty(roles)){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
List<String> roles = Convert.toList(String.class, cache);
if(null == roles){
return ListUtil.empty();
}
return roles;
}
@ -399,60 +275,24 @@ public class UserUtil {
userId = currUser.getSwitchTenantUserId();
}
// 缓存Key
String cacheKey = PREFIX_ID_PERMISSIONS + userId;
List<String> permissions;
// 先从缓存里拿
Object obj = CacheUtil.getTimed(cacheKey);
permissions = Convert.toList(String.class, obj);
if(CollUtil.isNotEmpty(permissions)){
return permissions;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return ListUtil.empty();
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return ListUtil.empty();
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
obj = CacheUtil.getTimed(cacheKey);
permissions = Convert.toList(String.class, obj);
if(CollUtil.isNotEmpty(permissions)){
return permissions;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_PERMISSIONS + userId);
final String finalUserId = userId;
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<List<String>> resultVo = userRoleRefApi.getAllPerms(userId);
if(resultVo.isSuccess()){
permissions = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, permissions);
ResultVo<List<String>> resultVo = userRoleRefApi.getAllPerms(finalUserId);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return resultVo.getData();
}, true);
if(CollUtil.isEmpty(permissions)){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
List<String> permissions = Convert.toList(String.class, cache);
if(null == permissions){
return ListUtil.empty();
}
return permissions;
}
@ -473,61 +313,24 @@ public class UserUtil {
userId = currUser.getSwitchTenantUserId();
}
// 缓存Key
String cacheKey = PREFIX_ID_ORGS + userId;
List<UserOrgRefModel> orgList;
// 先从缓存里拿
Object obj = CacheUtil.getTimed(cacheKey);
orgList = Convert.toList(UserOrgRefModel.class, obj);
if(CollUtil.isNotEmpty(orgList)){
return orgList;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return ListUtil.empty();
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_ORGS + userId);
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return ListUtil.empty();
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
obj = CacheUtil.getTimed(cacheKey);
orgList = Convert.toList(UserOrgRefModel.class, obj);
if(CollUtil.isNotEmpty(orgList)){
return orgList;
}
final String finalUserId = userId;
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<List<UserOrgRefModel>> resultVo = userOrgRefApi.findListByUserId(userId);
if(resultVo.isSuccess()){
orgList = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, orgList);
ResultVo<List<UserOrgRefModel>> resultVo = userOrgRefApi.findListByUserId(finalUserId);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return resultVo.getData();
}, true);
if(CollUtil.isEmpty(orgList)){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
List<UserOrgRefModel> orgList = Convert.toList(UserOrgRefModel.class, cache);
if(null == orgList){
return ListUtil.empty();
}
return orgList;
}
@ -561,61 +364,24 @@ public class UserUtil {
userId = currUser.getSwitchTenantUserId();
}
// 缓存Key
String cacheKey = PREFIX_ID_MENUS + userId;
List<MenuModel> menus;
// 先从缓存里拿
Object obj = CacheUtil.getTimed(cacheKey);
menus = Convert.toList(MenuModel.class, obj);
if(CollUtil.isNotEmpty(menus)){
return menus;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return ListUtil.empty();
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return ListUtil.empty();
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
obj = CacheUtil.getTimed(cacheKey);
menus = Convert.toList(MenuModel.class, obj);
if(CollUtil.isNotEmpty(menus)){
return menus;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_MENUS + userId);
final String finalUserId = userId;
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<List<MenuModel>> resultVo = userRoleRefApi.getMenuListByUserId(userId);
if(resultVo.isSuccess()){
menus = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, menus);
ResultVo<List<MenuModel>> resultVo = userRoleRefApi.getMenuListByUserId(finalUserId);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return resultVo.getData();
}, true);
if(CollUtil.isEmpty(menus)){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
List<MenuModel> menus = Convert.toList(MenuModel.class, cache);
if(null == menus){
return ListUtil.empty();
}
return menus;
}
@ -637,57 +403,21 @@ public class UserUtil {
userId = currUser.getSwitchTenantUserId();
}
// 缓存Key
String cacheKey = PREFIX_ID_DEF_ROLE + userId;
// 先从缓存里拿
RoleModel roleModel = CacheUtil.getTimed(RoleModel.class, cacheKey);
if (roleModel != null){
return roleModel;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return null;
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
roleModel = CacheUtil.getTimed(RoleModel.class, cacheKey);
if (roleModel != null){
return roleModel;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_DEF_ROLE + userId);
final String finalUserId = userId;
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<RoleModel> resultVo = userRoleRefApi.getDefRoleByUserId(userId);
if(resultVo.isSuccess()){
roleModel = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, roleModel);
ResultVo<RoleModel> resultVo = userRoleRefApi.getDefRoleByUserId(finalUserId);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(),e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
if(roleModel == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
return null;
}
return resultVo.getData();
}, true);
return roleModel;
return Convert.convert(RoleModel.class, cache);
}
@ -710,56 +440,19 @@ public class UserUtil {
}
// 缓存Key
String cacheKey = PREFIX_ID_DEF_ORG + userId;
// 先从缓存里拿
UserOrgRefModel orgModel = CacheUtil.getTimed(UserOrgRefModel.class, cacheKey);
if (orgModel != null){
return orgModel;
}
// 拿不到 --------
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return null;
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return null;
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
orgModel = CacheUtil.getTimed(UserOrgRefModel.class, cacheKey);
if (orgModel != null){
return orgModel;
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_DEF_ORG + userId);
final String finalUserId = userId;
Object cache = SecurityCache.get(redisTemplate, cacheKey, (k) -> {
// 查询数据库
ResultVo<UserOrgRefModel> resultVo = userOrgRefApi.getDefOrgByUserId(userId);
if(resultVo.isSuccess()){
orgModel = resultVo.getData();
// 存入缓存
CacheUtil.put(cacheKey, orgModel);
ResultVo<UserOrgRefModel> resultVo = userOrgRefApi.getDefOrgByUserId(finalUserId);
if(!resultVo.isSuccess()){
return null;
}
}catch (Exception e){
log.error(e.getMessage(),e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
if(orgModel == null){
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
return null;
}
return resultVo.getData();
}, true);
return orgModel;
return Convert.convert(UserOrgRefModel.class, cache);
}
@ -781,33 +474,11 @@ public class UserUtil {
}
// 缓存Key
String cacheKey = PREFIX_ID + user.getId();
try {
// 存入缓存
CacheUtil.put(cacheKey, user);
}catch (Exception e){
log.error(e.getMessage(), e);
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return false;
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID + user.getId());
// 存入缓存
SecurityCache.put(redisTemplate, cacheKey, user);
// 存入缓存
flag = CacheUtil.put(cacheKey, user);
}catch (Exception e){
flag = false;
log.error(e.getMessage(), e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
return flag;
return true;
}
/**
@ -824,49 +495,22 @@ public class UserUtil {
return true;
}
UserModel userModelById = CacheUtil.getTimed(
UserModel.class, PREFIX_ID + user.getId());
UserModel userModelByUsername = CacheUtil.getTimed(
UserModel.class, PREFIX_USERNAME + user.getUsername());
boolean hasNilFlagById = CacheUtil.hasNilFlag(PREFIX_ID + user.getId());
boolean hasNilFlagByName = CacheUtil.hasNilFlag(PREFIX_USERNAME + user.getUsername());
String cacheKeyByUserId = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID + user.getId());
String cacheKeyByUsername = CacheUtil.formatKey(RedisConstants.PREFIX_USERNAME + user.getUsername());
// 计数器
int count = 0;
if (hasNilFlagById){
count++;
int count = 2;
{
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID + user.getId());
boolean tmp = SecurityCache.remove(redisTemplate, cacheKeyByUserId);
if(tmp){
count--;
}
}
if (hasNilFlagByName){
count++;
{
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_USERNAME + user.getUsername());
if(tmp){
count--;
}
}
// 只要有一个不为空 则执行刷新
if (userModelById != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID + user.getId());
if(tmp){
count--;
}
}
if (userModelByUsername != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_USERNAME + user.getUsername());
boolean tmp = SecurityCache.remove(redisTemplate, cacheKeyByUsername);
if(tmp){
count--;
}
@ -886,32 +530,9 @@ public class UserUtil {
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
Object obj = CacheUtil.getTimed(PREFIX_ID_ROLES + userId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_ROLES + userId);
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_AND_ROLES + userId);
// 计数器
int count = 0;
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID_ROLES + userId);
if(tmp){
count--;
}
}
if(obj != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID_ROLES + userId);
if(tmp){
count--;
}
}
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
/**
@ -928,32 +549,9 @@ public class UserUtil {
return true;
}
// 计数器
int count = 0;
RoleModel roleModel = CacheUtil.getTimed(RoleModel.class, PREFIX_ID_DEF_ROLE + userId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_DEF_ROLE + userId);
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_DEF_ROLE + userId);
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID_DEF_ROLE + userId);
if(tmp){
count--;
}
}
if(roleModel != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID_DEF_ROLE + userId);
if(tmp){
count--;
}
}
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
@ -968,34 +566,9 @@ public class UserUtil {
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_PERMISSIONS + userId);
Object obj = CacheUtil.getTimed(PREFIX_ID_PERMISSIONS + userId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_PERMISSIONS + userId);
// 计数器
int count = 0;
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID_PERMISSIONS + userId);
if(tmp){
count--;
}
}
if(obj != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID_PERMISSIONS + userId);
if(tmp){
count--;
}
}
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
/**
@ -1012,31 +585,9 @@ public class UserUtil {
return true;
}
// 计数器
int count = 0;
UserOrgRefWebModel orgRefModel = CacheUtil.getTimed(UserOrgRefWebModel.class, PREFIX_ID_ORGS + userId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_ORGS + userId);
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID_ORGS + userId);
if(tmp){
count--;
}
}
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_ORGS + userId);
if(orgRefModel != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID_ORGS + userId);
if(tmp){
count--;
}
}
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
/**
@ -1053,32 +604,9 @@ public class UserUtil {
return true;
}
// 计数器
int count = 0;
UserOrgRefModel orgModel = CacheUtil.getTimed(UserOrgRefModel.class, PREFIX_ID_DEF_ORG + userId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_DEF_ORG + userId);
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_DEF_ORG + userId);
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID_DEF_ORG + userId);
if(tmp){
count--;
}
}
if(orgModel != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID_DEF_ORG + userId);
if(tmp){
count--;
}
}
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
@ -1092,33 +620,9 @@ public class UserUtil {
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
String cacheKey = CacheUtil.formatKey(RedisConstants.PREFIX_USER_ID_MENUS + userId);
Object obj = CacheUtil.getTimed(PREFIX_ID_MENUS + userId);
boolean hasNilFlag = CacheUtil.hasNilFlag(PREFIX_ID_MENUS + userId);
// 计数器
int count = 0;
// 只要不为空 则执行刷新
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(PREFIX_ID_MENUS + userId);
if(tmp){
count--;
}
}
if(obj != null){
count++;
// 先删除
boolean tmp = CacheUtil.del(PREFIX_ID_MENUS + userId);
if(tmp){
count--;
}
}
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
/**
@ -1201,7 +705,8 @@ public class UserUtil {
public void init(GlobalProperties globalProperties,
UserApi userApi,
UserRoleRefApi userRoleRefApi,
UserOrgRefApi userOrgRefApi){
UserOrgRefApi userOrgRefApi,
RedisTemplate<String, Object> redisTemplate) {
if(globalProperties != null && globalProperties.getAuth() != null
&& globalProperties.getAuth().getToken() != null
){
@ -1210,11 +715,9 @@ public class UserUtil {
}
UserUtil.userApi = userApi;
UserUtil.userRoleRefApi = userRoleRefApi;
UserUtil.userOrgRefApi = userOrgRefApi;
UserUtil.redisTemplate = redisTemplate;
IS_INIT = true;
}

@ -20,16 +20,18 @@ import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.convert.Convert;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.opsli.core.cache.CacheUtil;
import org.opsli.core.cache.SecurityCache;
import org.opsli.core.msg.CoreMsg;
import org.opsli.core.utils.DistributedLockUtil;
import org.opsli.core.utils.ThrowExceptionUtil;
import org.opsli.modulars.generator.template.service.IGenTemplateDetailService;
import org.opsli.modulars.generator.template.wrapper.GenTemplateDetailModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
@ -53,11 +55,13 @@ public class GenTemplateUtil {
private static boolean IS_INIT;
/** 缓存前缀 NAME */
private static final String CACHE_PREFIX_NAME = "gen:template:";
private static final String CACHE_PREFIX_NAME = "hash#{}:gen:template:";
/** 代码模板明细 Service */
private static IGenTemplateDetailService genTemplateDetailService;
private static RedisTemplate<String, Object> redisTemplate;
/**
* ID
* @param parentId ID
@ -69,77 +73,21 @@ public class GenTemplateUtil {
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 缓存Key
String cacheKey = CACHE_PREFIX_NAME + parentId;
// 处理集合数据
List<GenTemplateDetailModel> wrapperModels = handleDictList(
CacheUtil.getHashAll(cacheKey), parentId);
if(CollUtil.isNotEmpty(wrapperModels)){
return sortWrappers(wrapperModels);
}
// 防止缓存穿透判断
boolean hasNilFlag = CacheUtil.hasNilFlag(cacheKey);
if(hasNilFlag){
return sortWrappers(wrapperModels);
}
try {
// 分布式加锁
if(!DistributedLockUtil.lock(cacheKey)){
// 无法申领分布式锁
log.error(CoreMsg.REDIS_EXCEPTION_LOCK.getMessage());
return sortWrappers(wrapperModels);
}
// 如果获得锁 则 再次检查缓存里有没有, 如果有则直接退出, 没有的话才发起数据库请求
// 处理集合数据
wrapperModels = handleDictList(
CacheUtil.getHashAll(cacheKey), parentId);
if(CollUtil.isNotEmpty(wrapperModels)){
return sortWrappers(wrapperModels);
}
String cacheKey = CacheUtil.formatKey(CACHE_PREFIX_NAME + parentId);
List<GenTemplateDetailModel> listByParent = genTemplateDetailService.findListByParent(parentId);
Map<String, Object> templateCache = SecurityCache.hGetAll(redisTemplate, cacheKey, (k) -> {
// 处理数据库查询数据
if(CollUtil.isNotEmpty(listByParent)){
wrapperModels = listByParent;
// 计数器
int count = wrapperModels.size();
for (GenTemplateDetailModel model : wrapperModels) {
// 保存至缓存
boolean ret = GenTemplateUtil.put(model);
if(ret){
count--;
}
}
// 回滚 清空缓存
if(count != 0){
for (GenTemplateDetailModel model : wrapperModels) {
GenTemplateUtil.del(model);
}
}
return sortWrappers(wrapperModels);
List<GenTemplateDetailModel> listByParent = genTemplateDetailService.findListByParent(parentId);
Map<String, Object> templateMap = Maps.newHashMapWithExpectedSize(listByParent.size());
for (GenTemplateDetailModel genTemplateDetailModel : listByParent) {
templateMap.put(genTemplateDetailModel.getId(), genTemplateDetailModel);
}
return templateMap;
});
}catch (Exception e){
log.error(e.getMessage(),e);
}finally {
// 释放锁
DistributedLockUtil.unlock(cacheKey);
}
// 如果值还是 为空 则赋默认值
if(CollUtil.isEmpty(wrapperModels)){
// 加入缓存防穿透
// 设置空变量 用于防止穿透判断
CacheUtil.putNilFlag(cacheKey);
}
// 排序
// 处理集合数据
List<GenTemplateDetailModel> wrapperModels = handleDictList(templateCache);
return sortWrappers(wrapperModels);
}
@ -150,8 +98,8 @@ public class GenTemplateUtil {
*/
private static List<GenTemplateDetailModel> sortWrappers(List<GenTemplateDetailModel> wrapperModels) {
// 非法判读
if(wrapperModels == null){
return null;
if(CollUtil.isEmpty(wrapperModels)){
return ListUtil.empty();
}
return ListUtil.sort(wrapperModels,
@ -162,22 +110,6 @@ public class GenTemplateUtil {
// ===============
/**
*
* @param model
*/
private static boolean put(GenTemplateDetailModel model){
// 判断 工具类是否初始化完成
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
// 清除缓存
GenTemplateUtil.del(model);
return CacheUtil.putHash(CACHE_PREFIX_NAME + model.getParentId(),
model.getId(), model);
}
/**
*
* @param model
@ -192,36 +124,10 @@ public class GenTemplateUtil {
return true;
}
boolean hasNilFlag = CacheUtil.hasNilFlag(CACHE_PREFIX_NAME +
model.getParentId() + ":" + model.getId());
GenTemplateDetailModel templateDetailModel = CacheUtil.getHash(GenTemplateDetailModel.class,
CACHE_PREFIX_NAME + model.getParentId(),
model.getId());
// 计数器
int count = 0;
if (hasNilFlag){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delNilFlag(CACHE_PREFIX_NAME +
model.getParentId() + ":" + model.getId());
if(tmp){
count--;
}
}
if (templateDetailModel != null){
count++;
// 清除空拦截
boolean tmp = CacheUtil.delHash(CACHE_PREFIX_NAME +
model.getParentId(), model.getId());
if(tmp){
count--;
}
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(CACHE_PREFIX_NAME + model.getParentId());
return count == 0;
return SecurityCache.hDel(redisTemplate, cacheKey, model.getId());
}
/**
@ -234,29 +140,18 @@ public class GenTemplateUtil {
ThrowExceptionUtil.isThrowException(!IS_INIT,
CoreMsg.OTHER_EXCEPTION_UTILS_INIT);
List<GenTemplateDetailModel> wrapperList = GenTemplateUtil.getTemplateDetailList(parentId);
if(CollUtil.isEmpty(wrapperList)){
return true;
}
// 缓存Key
String cacheKey = CacheUtil.formatKey(CACHE_PREFIX_NAME + parentId);
// 计数器
int count = wrapperList.size();
for (GenTemplateDetailModel wrapperModel : wrapperList) {
boolean tmp = GenTemplateUtil.del(wrapperModel);
if(tmp){
count--;
}
}
return count == 0;
return SecurityCache.remove(redisTemplate, cacheKey);
}
/***
*
* @param tempMap Map
* @param id ID
* @return List
*/
public static List<GenTemplateDetailModel> handleDictList(Map<String, Object> tempMap, String id){
public static List<GenTemplateDetailModel> handleDictList(Map<String, Object> tempMap){
List<GenTemplateDetailModel> wrapperModels = Lists.newArrayList();
if(CollUtil.isNotEmpty(tempMap)){
for (Map.Entry<String, Object> entry : tempMap.entrySet()) {
@ -278,8 +173,10 @@ public class GenTemplateUtil {
*
*/
@Autowired
public void init(IGenTemplateDetailService genTemplateDetailService) {
public void init(IGenTemplateDetailService genTemplateDetailService,
RedisTemplate<String, Object> redisTemplate) {
GenTemplateUtil.genTemplateDetailService = genTemplateDetailService;
GenTemplateUtil.redisTemplate = redisTemplate;
IS_INIT = true;
}

@ -1,10 +1,4 @@
{
"properties": [
{
"name": "spring.redis.pushsub.enable",
"type": "java.lang.Boolean",
"defaultValue": false,
"description": "开启消息订阅."
}
]
}

@ -54,21 +54,11 @@ spring:
# allow-bean-definition-overriding: true
# 缓存配置项
cache-conf:
cache:
# 前缀
prefix: opsli
# 一级缓存 ---- EhCache 配置
cache:
# 是否启用本地缓存 (默认不启用, 如果业务对于缓存依赖较高可启用本地缓存作为一级缓存)
enable: false
# 加载配置文件
jcache:
config: classpath:config/ehcache-opsli.xml
# 二级缓存 ---- Redis 配置
redis:
# 开启消息订阅
pushsub:
enable: true
lettuce:
pool:
max-active: 8 #最大连接数据库连接数,设 0 为没有限制

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd">
<!--指定缓存持久化目录-->
<persistence directory="${java.io.tmpdir}/opsli-ehcache-data"/>
<!-- <cache-template>可以让你创建一个抽象的<cache>配置文件,该配置文件可以进一步的被扩展。-->
<!-- 默认缓存模版 -->
<cache-template name="opsliDefaults">
<!--键值对被声明为字符串类型如果没有指明默认是Object类型。-->
<key-type>java.lang.String</key-type>
<value-type>java.lang.Object</value-type>
<resources>
<heap unit="entries">2000</heap> <!--配置堆储存-->
<!-- <offheap unit="MB">300</offheap> &lt;!&ndash;配置堆外储存&ndash;&gt;-->
</resources>
</cache-template>
<!-- timed 时效数据它使用名为opsliDefaults的<cache-template>,并将其主键覆盖到更广泛的类型 -->
<cache alias="timed" uses-template="opsliDefaults">
<!--缓存到期配置-->
<expiry>
<!-- 只允许配秒 默认 21600秒6小时 -->
<ttl unit="seconds">21600</ttl>
</expiry>
</cache>
</config>
Loading…
Cancel
Save