提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


目的

因为根据项目需求需要进行国产化适配,redis安装在国产化系统比较麻烦,并且不能通过国产化测评,所以现在对redis进行剥离。


一、去除redis 配置

去除ruoyi-admin下application.yml的redis配置!
在这里插入图片描述

二、去除ruoyi-framework下RedisConfig的配置

直接注释@Bean、@Configuration和@EnableCaching注解就可以了,不用删除RedisConfig,防止后期又要使用redis,直接放开就可以了。
在这里插入图片描述

三、在ruoyi-common的core/redis下新建MyMapCache类

@Component
public class MyMapCache{

    public ConcurrentHashMap<String, Object> concurrentHashMap = new ConcurrentHashMap<String, Object>();

    /**
     * 放入值
     *
     * @param key
     * @param value
     */
    public void put(String key, Object value) {
        concurrentHashMap.put(key, value);
    }

    /**
     * 取值
     *
     * @param key
     * @return
     */
    public Object get(String key) {
        return concurrentHashMap.get(key);
    }

    /**
     * 移除
     *
     * @param k
     */
    public void remove(String key) {
        concurrentHashMap.remove(key);
    }

}

四、关于RedisCache类

暂时用不到,但是为了防止系统报错,暂时不需要整改和删除。

五、修改ruoyi-common下utils/DictUtils

public class DictUtils {
    /**
     * 分隔符
     */
    public static final String SEPARATOR = ",";

    /**
     * 设置字典缓存
     *
     * @param key       参数键
     * @param dictDatas 字典数据列表
     */
    public static void setDictCache(String key, List<SysDictData> dictDatas) {
        SpringUtils.getBean(MyMapCache.class).put(getCacheKey(key), dictDatas);
    }

    /**
     * 获取字典缓存
     *
     * @param key 参数键
     * @return dictDatas 字典数据列表
     */
    public static List<SysDictData> getDictCache(String key) {
        Object cacheObj = SpringUtils.getBean(MyMapCache.class).get(getCacheKey(key));
        if (StringUtils.isNotNull(cacheObj)) {
            List<SysDictData> dictDatas = StringUtils.cast(cacheObj);
            return dictDatas;
        }
        return null;
    }


    /**
     * 根据字典类型和字典值获取字典标签
     *
     * @param dictType  字典类型
     * @param dictValue 字典值
     * @return 字典标签
     */
    public static String getDictLabel(String dictType, String dictValue) {
        return getDictLabel(dictType, dictValue, SEPARATOR);
    }

    /**
     * 根据字典类型和字典标签获取字典值
     *
     * @param dictType  字典类型
     * @param dictLabel 字典标签
     * @return 字典值
     */
    public static String getDictValue(String dictType, String dictLabel) {
        return getDictValue(dictType, dictLabel, SEPARATOR);
    }

    /**
     * 根据字典类型和字典值获取字典标签
     *
     * @param dictType  字典类型
     * @param dictValue 字典值
     * @param separator 分隔符
     * @return 字典标签
     */
    public static String getDictLabel(String dictType, String dictValue, String separator) {
        StringBuilder propertyString = new StringBuilder();
        List<SysDictData> datas = getDictCache(dictType);

        if (StringUtils.containsAny(separator, dictValue) && StringUtils.isNotEmpty(datas)) {
            for (SysDictData dict : datas) {
                for (String value : dictValue.split(separator)) {
                    if (value.equals(dict.getDictValue())) {
                        propertyString.append(dict.getDictLabel() + separator);
                        break;
                    }
                }
            }
        } else {
            for (SysDictData dict : datas) {
                if (dictValue.equals(dict.getDictValue())) {
                    return dict.getDictLabel();
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }

    /**
     * 根据字典类型和字典标签获取字典值
     *
     * @param dictType  字典类型
     * @param dictLabel 字典标签
     * @param separator 分隔符
     * @return 字典值
     */
    public static String getDictValue(String dictType, String dictLabel, String separator) {
        StringBuilder propertyString = new StringBuilder();
        List<SysDictData> datas = getDictCache(dictType);

        if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas)) {
            for (SysDictData dict : datas) {
                for (String label : dictLabel.split(separator)) {
                    if (label.equals(dict.getDictLabel())) {
                        propertyString.append(dict.getDictValue() + separator);
                        break;
                    }
                }
            }
        } else {
            for (SysDictData dict : datas) {
                if (dictLabel.equals(dict.getDictLabel())) {
                    return dict.getDictValue();
                }
            }
        }
        return StringUtils.stripEnd(propertyString.toString(), separator);
    }

    /**
     * 删除指定字典缓存
     *
     * @param key 字典键
     */
    public static void removeDictCache(String key) {
        List<String> keys = new ArrayList<>();
        MyCache myMapCache = SpringUtils.getBean(MyCache.class);
        for (Map.Entry<String, Object> entry : myMapCache.concurrentHashMap.entrySet()) {
            if (entry.getKey().startsWith(Constants.SYS_DICT_KEY)) {
                keys.add(entry.getKey());
            }
        }
        keys.forEach(item -> {
            if (item.equals(key)) {
                myMapCache.remove(item);
            }
        });
    }

    /**
     * 清空字典缓存
     */
    public static void clearDictCache() {
        List<String> keys = new ArrayList<>();
        MyMapCache myMapCache = SpringUtils.getBean(MyCache.class);
        for (Map.Entry<String, Object> entry : myMapCache.concurrentHashMap.entrySet()) {
            if (entry.getKey().startsWith(Constants.SYS_DICT_KEY)) {
                keys.add(entry.getKey());
            }
        }
        keys.forEach(item -> {
            myMapCache.remove(item);
        });
    }

    /**
     * 设置cache key
     *
     * @param configKey 参数键
     * @return 缓存键key
     */
    public static String getCacheKey(String configKey) {
        return Constants.SYS_DICT_KEY + configKey;
    }

六、修改基于redis的限流处理,使用令牌桶算法进行限流

@Aspect
@Component
public class RateLimiterAspect {
    private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);

    private ConcurrentMap<String, TokenBucket> buckets = new ConcurrentHashMap<>();

    @Before("@annotation(rateLimiter)")
    public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {
        String key = rateLimiter.key();
        int time = rateLimiter.time();
        int count = rateLimiter.count();

        String combineKey = getCombineKey(rateLimiter, point);
        TokenBucket bucket = buckets.computeIfAbsent(combineKey, k -> new TokenBucket(count, time));

        if (!bucket.tryConsume()) {
            throw new ServiceException("访问过于频繁,请稍候再试");
        }

        log.info("限制请求'{}',当前请求数'{}',缓存key'{}'", count, bucket.getTokens(), key);
    }

    public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {
        StringBuilder stringBuilder = new StringBuilder(rateLimiter.key());
        if (rateLimiter.limitType() == LimitType.IP) {
            stringBuilder.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-");
        }
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> targetClass = method.getDeclaringClass();
        stringBuilder.append(targetClass.getName()).append("-").append(method.getName());
        return stringBuilder.toString();
    }

    private static class TokenBucket {
        private final int capacity;
        private long lastRefillTime;
        private double tokens;

        TokenBucket(int capacity, long refillInterval) {
            this.capacity = capacity;
            this.tokens = capacity;
            this.lastRefillTime = System.currentTimeMillis();
            // Start a background thread to refill the bucket periodically
            Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(
                    () -> {
                        synchronized (TokenBucket.this) {
                            long now = System.currentTimeMillis();
                            double elapsedSeconds = (now - lastRefillTime) / 1000.0;
                            tokens = Math.min(capacity, tokens + elapsedSeconds * capacity / refillInterval);
                            lastRefillTime = now;
                        }
                    },
                    refillInterval,
                    refillInterval,
                    TimeUnit.MILLISECONDS
            );
        }

        synchronized boolean tryConsume() {
            if (tokens > 0) {
                tokens--;
                return true;
            } else {
                return false;
            }
        }

        synchronized int getTokens() {
            return (int) tokens;
        }
    }
 }

七、修改ruoyi-framework下TokenService


@Component
public class TokenService
{
    // 令牌自定义标识
    @Value("${token.header}")
    private String header;

    // 令牌秘钥
    @Value("${token.secret}")
    private String secret;

    // 令牌有效期(默认30分钟)
    @Value("${token.expireTime}")
    private int expireTime;

    // 是否允许账户多终端同时登录(true允许 false不允许)
    @Value("${token.soloLogin}")
    private boolean soloLogin;

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;

    @Autowired
    MyMapCache myMapCache;


    /**
     * 获取用户身份信息
     *
     * @return 用户信息
     */
    public LoginUser getLoginUser(HttpServletRequest request)
    {
        // 获取请求携带的令牌
        String token = getToken(request);
        if (StringUtils.isNotEmpty(token))
        {
            Claims claims = parseToken(token);
            // 解析对应的权限以及用户信息
            String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
            String userKey = getTokenKey(uuid);
            LoginUser user = (LoginUser) myMapCache.get(userKey);
            return user;
        }
        return null;
    }

    public LoginUser getLoginUser(String token){
        if (StringUtils.isNotEmpty(token)){
            Claims claims = parseToken(token);
            // 解析对应的权限以及用户信息
            String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
            String userKey = getTokenKey(uuid);
            LoginUser user = (LoginUser) myMapCache.get(userKey);
            return user;
        }
        return null;
    }

    /**
     * 设置用户身份信息
     */
    public void setLoginUser(LoginUser loginUser)
    {
        if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken()))
        {
            refreshToken(loginUser);
        }
    }

    /**
     * 删除用户身份信息
     */
    public void delLoginUser(String token,Long userId)
    {
    	if (StringUtils.isNotEmpty(token))
    	{
    		String userKey = getTokenKey(token);
    		myMapCache.remove(token);
    		myMapCache.remove(userKey);
    	}
    	if (!soloLogin && StringUtils.isNotNull(userId))
    	{
    		String userIdKey = getUserIdKey(userId);
    		myMapCache.remove(userIdKey);
    	}
    }

     /**
     * 创建令牌
     *
     * @param loginUser 用户信息
     * @return 令牌
     * (在Constants定义一个    
     * public static final String LOGIN_USERID_KEY = "login_userid:";)
     */
    private String getUserIdKey(Long userId)
    {
    	return Constants.LOGIN_USERID_KEY + userId;
    }

    /**
     * 创建令牌
     *
     * @param loginUser 用户信息
     * @return 令牌
     */
    public String createToken(LoginUser loginUser)
    {
        String token = IdUtils.fastUUID();
        loginUser.setToken(token);
        setUserAgent(loginUser);
        refreshToken(loginUser);

        Map<String, Object> claims = new HashMap<>();
        claims.put(Constants.LOGIN_USER_KEY, token);
        return createToken(claims);
    }

    /**
     * 验证令牌有效期,相差不足20分钟,自动刷新缓存
     *
     * @param loginUser
     * @return 令牌
     */
    public void verifyToken(LoginUser loginUser)
    {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
        {
            refreshToken(loginUser);
        }
    }

    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(LoginUser loginUser)
    {
    	loginUser.setLoginTime(System.currentTimeMillis());
    	loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
    	// 根据uuid将loginUser缓存
    	String userKey = getTokenKey(loginUser.getToken());
    	myMapCache.put(userKey, loginUser);
    	if (!soloLogin)
    	{
    		// 缓存用户唯一标识,防止同一帐号,同时登录
    		String userIdKey = getUserIdKey(loginUser.getUser().getUserId());
    		myMapCache.put(userIdKey, userKey);
    	}
    }

    /**
     * 设置用户代理信息
     *
     * @param loginUser 登录信息
     */
    public void setUserAgent(LoginUser loginUser)
    {
        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        loginUser.setIpaddr(ip);
        loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
        loginUser.setBrowser(userAgent.getBrowser().getName());
        loginUser.setOs(userAgent.getOperatingSystem().getName());
    }

    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private String createToken(Map<String, Object> claims)
    {
        String token = Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, secret).compact();
        return token;
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private Claims parseToken(String token)
    {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token)
    {
        Claims claims = parseToken(token);
        return claims.getSubject();
    }

    /**
     * 获取请求token
     *
     * @param request
     * @return token
     */
    private String getToken(HttpServletRequest request)
    {
        String token = request.getHeader(header);
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
        {
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
    }

    private String getTokenKey(String uuid)
    {
        return Constants.LOGIN_TOKEN_KEY + uuid;
    }

Logo

快速构建 Web 应用程序

更多推荐