Idea+maven+spring-cloud项目搭建系列--2.1gateway网关路由,鉴权,日志记录

本文是建立在微服务中已经整合redis 的基础之上进行的处理,如果没有项目没有整合redis 可以参考:https://blog.csdn.net/l123lgx/article/details/121521855
本文是建立在spring-aop基础只上进行的处理,spring-aop切面参考文档:https://blog.csdn.net/l123lgx/article/details/106399377

本文使用了nacos 作为微服务的注册与发现,nacos 阿里云服务器的安装可以参考:https://blog.csdn.net/l123lgx/article/details/121421431
nacos 服务端的配置和使用可以参考:
https://blog.csdn.net/l123lgx/article/details/121491529

1 gateway 介绍:

  • Route 路由:gateway的基本构建模块。它由ID、目标URI、断言集合和过滤器集合组成。如果聚合断言结果为真,则匹配到该路由。
  • Predicate 断言:这是一个Java 8 Function Predicate。输入类型是 Spring Framework ServerWebExchange。这允许开发人员可以匹配来自HTTP请求的任何内容,例如Header或参数。
  • Filter 过滤器:这些是使用特定工厂构建的 Spring FrameworkGatewayFilter实例。所以可以在返回请求之前或之后修改请求和响应的内容
    Idea+maven+spring-cloud项目搭建系列--2.1gateway网关路由,鉴权,日志记录_第1张图片
    客户端向Spring Cloud Gateway发出请求。如果Gateway Handler Mapping确定请求与路由匹配,则将其发送到Gateway Web Handler。此handler通过特定于该请求的过滤器链处理请求。
    图中filters被虚线划分的原因是filters可以在发送代理请求之前或之后执行逻辑。先执行所有“pre filter”逻辑,然后进行请求代理。在请求代理执行完后,执行“post filter”逻辑。

2 gateway -nacos 配置文件增加路由:

spring:
  cloud: 
    gateway:
      routes:                       # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
        - id: bluegrass-coree         # 当前路由的标识, 要求唯一
          uri: lb://bluegrass-coree # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
          filters:
          # StripPrefix GatewayFilter Factory 包括一个parts参数。 parts参数指示在将请求发送到下游之前,要从请求中去除的路径中的节数
            - StripPrefix=1 
             # 当通过网关发出/bar/foo请求时,向bluegrass-core 服务发出的请求将是http://bluegrass-core/foo
          predicates:                # 断言(就是路由转发要满足的条件)
            - Path=/bluegrass-coree/**
      # 与服务注册与发现组件结合,设置为 true 后;id默认为微服务名称xxx-server;gatewayapplication有feign自动做负载均衡;
#不需要定制化显式配置;已生成默认的id=xservice,uri: lb://xserver , path=/serviceId/**
      # discovery:
      #   locator:
      #     enabled: true
      #     lowerCaseServiceId: true
      
  # redis 配置
  redis:
    host: localhost
    port: 6379
    timeout: 120000
    database: 1
    password: 123456
    lettuce:
      pool:
        max-active: 50
        max-idle: 8
        max-wait: -1
        min-idle: 1
# 这里配置token 验证白名单
token: 
  enable: true
  skip-urls-pattern: /notoken,

3 增加全局过滤器:
在gateway 服务中新增一个config 文件夹,并增加一个全局过滤器配置文件 CorsConfig
增加一个filter 全局过滤器文件夹:
Idea+maven+spring-cloud项目搭建系列--2.1gateway网关路由,鉴权,日志记录_第2张图片
3.1 增加全局跨域过滤器:
(3.1.1)CorsResponseHeaderFilter.java

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;

/**
* @Description TODO
* @Date 2021/11/26 15:01
* @Author lgx
* @Version 1.0
*/
public class CorsResponseHeaderFilter  implements GlobalFilter, Ordered {
 @Override
 public int getOrder() {
     // 指定此过滤器位于NettyWriteResponseFilter之后
     // 即待处理完响应体后接着处理响应头
     return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 5;
 }

 @Override
 @SuppressWarnings("serial")
 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
     return chain.filter(exchange).then(Mono.defer(() -> {
         exchange.getResponse().getHeaders().entrySet().stream()
                 .filter(kv -> (kv.getValue() != null && kv.getValue().size() > 1))
                 .filter(kv -> (kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)
                         || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)))
                 .forEach(kv ->
                 {
                     kv.setValue(new ArrayList<String>() {{add(kv.getValue().get(0));}});
                 });

         return chain.filter(exchange);
     }));
 }
}

(3.1.1)CorsConfig .java

import org.lgx.bluegrass.bluegrassgateway.filter.CorsResponseHeaderFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

/**
 * @Description TODO
 * @Date 2021/11/26 15:00
 * @Author lgx
 * @Version 1.0
 */
@Configuration
public class CorsConfig {
    private static final String ALLOWED_HEADERS = "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN,token,username,client";
    private static final String ALLOWED_METHODS = "*";
    private static final String ALLOWED_ORIGIN = "*";
    private static final String ALLOWED_Expose = "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN,token,username,client";
    private static final String MAX_AGE = "18000L";

    // 跨域配置
    @Bean
    public CorsResponseHeaderFilter corsResponseHeaderFilter() {
        //解决geteway重复添加跨域请求头的问题:https://blog.csdn.net/xht555/article/details/89484091
        return new CorsResponseHeaderFilter();
    }
    // 跨域配置
    @Bean
    public WebFilter corsFilter() {
        return (ServerWebExchange ctx, WebFilterChain chain) -> {
            ServerHttpRequest request = ctx.getRequest();
            if (CorsUtils.isCorsRequest(request)) {
                ServerHttpResponse response = ctx.getResponse();
                HttpHeaders headers = response.getHeaders();
                headers.set("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
                headers.set("Access-Control-Allow-Methods", ALLOWED_METHODS);
                headers.set("Access-Control-Max-Age", MAX_AGE);
                headers.set("Access-Control-Allow-Headers", ALLOWED_HEADERS);
                headers.set("Access-Control-Expose-Headers", ALLOWED_Expose);
                headers.set("Access-Control-Allow-Credentials", "fasle");
                if (request.getMethod() == HttpMethod.OPTIONS) {
                    response.setStatusCode(HttpStatus.OK);
                    return Mono.empty();
                }
            }
            return chain.filter(ctx);
        };

    }
}

3.2 增加全局登录状态验证过滤器:
(3.2.1) 增加依赖:
(3.2.1.1)gateway 网关服务pom 增加servlet jar 依赖:
Idea+maven+spring-cloud项目搭建系列--2.1gateway网关路由,鉴权,日志记录_第3张图片
(3.2.1.2)Api 模块pom增加 jwt jar 加密jar依赖
Idea+maven+spring-cloud项目搭建系列--2.1gateway网关路由,鉴权,日志记录_第4张图片
(3.2.1.3)Api模块增加工具类:
Idea+maven+spring-cloud项目搭建系列--2.1gateway网关路由,鉴权,日志记录_第5张图片
定义常量类ConstantApi,RedisKeyConstant.java

package org.lgx.bluegrass.api.constant;

public class RedisKeyConstant {

    public static String USER_DATA_PREFIX = "user_current_authority:";

    public static String USER_DATA_NO_ORG_PREFIX = "user_current_no_org_authority:";


}

ConstantApi.java

/**
 * @Description TODO
 * @Date 2021/11/26 15:35
 * @Author lgx
 * @Version 1.0
 */
public class ConstantApi {
    public static final String X_AUTHORIZATION = "X-Authorization";
}

JwtUtil.java

package org.lgx.bluegrass.api.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author liumeng
 */
public class JwtUtil {

    private JwtUtil() {
        throw new IllegalStateException("Utility class");
    }

    /**
     * token秘钥,请勿泄露,请勿随便修改
     */
    public static final String SECRET = "xhuser.135435234";

    /**
     * JWT生成Token.
* JWT构成: header, payload, signature * * @param uid 登录成功后用户uid, 参数uid不可传空 */
public static String createToken(String uid) { // sign time Date iatDate = new Date(); // header Map Map<String, Object> map = new HashMap<>(); map.put("alg", "HS256"); map.put("typ", "JWT"); return JWT.create().withHeader(map) .withClaim("iss", "xuhui") .withClaim("aud", "xuhui") .withClaim("uid", null == uid ? null : uid) .withIssuedAt(iatDate) .sign(Algorithm.HMAC256(SECRET)); } /** * 解密Token * * @param token * @return * @throws Exception */ public static Map<String, Claim> verifyToken(String token) { JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build(); DecodedJWT jwt = verifier.verify(token); return jwt.getClaims(); } /** * 根据Token获取user_id * * @param token * @return user_id */ public static String getUID(String token) { String uid = null; try { Map<String, Claim> claims = verifyToken(token); Claim uidClaim = claims.get("uid"); if (null != uidClaim && !"".equals(uidClaim.asString())) { uid = uidClaim.asString(); } } catch (Exception e) { uid = ""; } return uid; } public static boolean isNumeric(String s) { if (s != null && !"".equals(s.trim())) { return s.matches("^[0-9]*$"); } else { return false; } } }

UrlPathUtil.java

package org.lgx.bluegrass.api.util;

/**
 * @Description TODO
 * @Date 2021/11/26 15:46
 * @Author lgx
 * @Version 1.0
 */
public class UrlPathUtil {
    /**
     * @描述 url正则匹配,匹配则返回true, 跳过的相反
     * @参数[path, urlsPatterns, isSkip]
     * @返回值 boolean
     * @修改人和其他信息
     */
    public static boolean isUrlPathMatch(String path, String urlsPatterns, boolean isSkip) {
        String[] urlPattern = urlsPatterns.split(",");
        for (String pattern : urlPattern) {
            if (pattern != null && !pattern.isEmpty() && (path.contains(pattern) || path.matches(pattern))) {
                return isSkip ? false : true;
            }
        }
        return isSkip ? true : false;
    }
}

(3.2.1.4) gateway 网关服务增加过滤器及工具类
Idea+maven+spring-cloud项目搭建系列--2.1gateway网关路由,鉴权,日志记录_第6张图片

GatewayUtil.java

package org.lgx.bluegrass.bluegrassgateway.util;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import io.netty.buffer.ByteBufAllocator;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @创建人 lgx
 * @描述
 */
public class GatewayUtil {

    public static Mono<Void> responseErrorStatus(HttpStatus status, ServerHttpResponse serverHttpResponse, Integer code, String message) {
        serverHttpResponse.setStatusCode(status);
        Map<String, Object> mapData = new HashMap<>();
        mapData.put("code", code);
        mapData.put("message", message);
        DataBuffer bodyDataBuffer = GatewayUtil.stringBuffer(JSONObject.toJSONString(mapData, SerializerFeature.WriteNullBooleanAsFalse,
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteEnumUsingToString,
                SerializerFeature.DisableCircularReferenceDetect,
                SerializerFeature.WriteDateUseDateFormat));
        Flux<DataBuffer> bodyFlux = Flux.create((t) -> t.next(bodyDataBuffer).complete());
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=utf-8");
        serverHttpResponse.getHeaders().add("Date", new Date().toString());
        return serverHttpResponse.writeWith(bodyFlux);
    }

    public static DataBuffer stringBuffer(String value) {
        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
        if (value == null) {
            return nettyDataBufferFactory.allocateBuffer(0);
        }
        byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
        buffer.write(bytes);
        return buffer;
    }

    /**
     * @描述 nginx已使用X-Real-IP,使用上面的方法getIpAddress
     * @参数[request]
     * @返回值 java.lang.String
     * @创建人 孙中泉
     * @创建时间 2019/12/10
     * @修改人和其他信息
     */
    public static String getIpAddressAll(ServerHttpRequest request) {
        try {
            HttpHeaders headers = request.getHeaders();
            String ip = headers.getFirst("X-Original-Forwarded-For");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = headers.getFirst("X-Forwarded-For");
            }

            if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
                // 多次反向代理后会有多个ip值,第一个ip才是真实ip
                if (ip.indexOf(",") != -1) {
                    ip = ip.split(",")[0];
                }
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = headers.getFirst("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = headers.getFirst("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = headers.getFirst("HTTP_CLIENT_IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddress().getAddress().getHostAddress();
            }
            return ip == null ? "" : ip;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

}

RedisUtil.java

package org.lgx.bluegrass.bluegrassgateway.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

/**
 * Redis工具类  copy by liuhuan
 * modified
 *
 * @author
 * @version 1.1 (GitHub文档: https://github.com/whvcse/RedisUtil )
 * @date
 */
@Component
public class RedisUtil {

    @Autowired
    @Qualifier("stringRedisTemplate")
    private StringRedisTemplate redisTemplate;

    public void setRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public StringRedisTemplate getRedisTemplate() {
        return this.redisTemplate;
    }

    /** -------------------key相关操作--------------------- */

    /**
     * 删除key
     *
     * @param key
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 批量删除key
     *
     * @param keys
     */
    public void delete(Collection<String> keys) {
        redisTemplate.delete(keys);
    }

    /**
     * 序列化key
     *
     * @param key
     * @return
     */
    public byte[] dump(String key) {
        return redisTemplate.dump(key);
    }

    /**
     * 是否存在key
     *
     * @param key
     * @return
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param timeout
     * @param unit
     * @return
     */
    public Boolean expire(String key, long timeout, TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 多少秒失效
     *
     * @param key
     * @param timeout 秒为单位
     * @return
     */
    public Boolean expire(String key, long timeout) {
        return redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param date
     * @return
     */
    public Boolean expireAt(String key, Date date) {
        return redisTemplate.expireAt(key, date);
    }

    /**
     * 查找匹配的key
     *
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 将当前数据库的 key 移动到给定的数据库 db 当中
     *
     * @param key
     * @param dbIndex
     * @return
     */
    public Boolean move(String key, int dbIndex) {
        return redisTemplate.move(key, dbIndex);
    }

    /**
     * 移除 key 的过期时间,key 将持久保持
     *
     * @param key
     * @return
     */
    public Boolean persist(String key) {
        return redisTemplate.persist(key);
    }

    /**
     * 返回 key 的剩余的过期时间
     *
     * @param key
     * @param unit
     * @return
     */
    public Long getExpire(String key, TimeUnit unit) {
        return redisTemplate.getExpire(key, unit);
    }

    /**
     * 返回 key 的剩余的过期时间
     *
     * @param key
     * @return
     */
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 从当前数据库中随机返回一个 key
     *
     * @return
     */
    public String randomKey() {
        return redisTemplate.randomKey();
    }

    /**
     * 修改 key 的名称
     *
     * @param oldKey
     * @param newKey
     */
    public void rename(String oldKey, String newKey) {
        redisTemplate.rename(oldKey, newKey);
    }

    /**
     * 仅当 newkey 不存在时,将 oldKey 改名为 newkey
     *
     * @param oldKey
     * @param newKey
     * @return
     */
    public Boolean renameIfAbsent(String oldKey, String newKey) {
        return redisTemplate.renameIfAbsent(oldKey, newKey);
    }

    /**
     * 返回 key 所储存的值的类型
     *
     * @param key
     * @return
     */
    public DataType type(String key) {
        return redisTemplate.type(key);
    }

    /** -------------------string相关操作--------------------- */

    /**
     * 设置指定 key 的值
     *
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 指定秒
     *
     * @param key
     * @param value
     * @param seconds 多少秒失效
     */
    public void set(String key, String value, long seconds) {
        redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
    }

    /**
     * 获取指定 key 的值
     *
     * @param key
     * @return
     */
    public String get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 返回 key 中字符串值的子字符
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public String getRange(String key, long start, long end) {
        return redisTemplate.opsForValue().get(key, start, end);
    }

    /**
     * 将给定 key 的值设为 value ,并返回 key 的旧值(old value)
     *
     * @param key
     * @param value
     * @return
     */
    public String getAndSet(String key, String value) {
        return redisTemplate.opsForValue().getAndSet(key, value);
    }

    /**
     * 对 key 所储存的字符串值,获取指定偏移量上的位(bit)
     *
     * @param key
     * @param offset
     * @return
     */
    public Boolean getBit(String key, long offset) {
        return redisTemplate.opsForValue().getBit(key, offset);
    }

    /**
     * 批量获取
     *
     * @param keys
     * @return
     */
    public List<String> multiGet(Collection<String> keys) {
        return redisTemplate.opsForValue().multiGet(keys);
    }

    /**
     * 设置ASCII码, 字符串'a'的ASCII码是97, 转为二进制是'01100001', 此方法是将二进制第offset位值变为value
     *
     * @param key
     * @param value 值,true为1, false为0
     * @return
     */
    public boolean setBit(String key, long offset, boolean value) {
        return redisTemplate.opsForValue().setBit(key, offset, value);
    }

    /**
     * 将值 value 关联到 key ,并将 key 的过期时间设为 timeout
     *
     * @param key
     * @param value
     * @param timeout 过期时间
     * @param unit    时间单位, 天:TimeUnit.DAYS 小时:TimeUnit.HOURS 分钟:TimeUnit.MINUTES
     *                秒:TimeUnit.SECONDS 毫秒:TimeUnit.MILLISECONDS
     */
    public void setEx(String key, String value, long timeout, TimeUnit unit) {
        redisTemplate.opsForValue().set(key, value, timeout, unit);
    }

    /**
     * 设置并失效
     *
     * @param key
     * @param value
     * @param secconds 多少秒失效
     */
    public void setEx(String key, String value, long secconds) {
        redisTemplate.opsForValue().set(key, value, secconds, TimeUnit.SECONDS);
    }

    /**
     * 只有在 key 不存在时设置 key 的值
     *
     * @param key
     * @param value
     * @return 之前已经存在返回false, 不存在返回true
     */
    public boolean setIfAbsent(String key, String value) {
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }

    /**
     * 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始
     *
     * @param key
     * @param value
     * @param offset 从指定位置开始覆写
     */
    public void setRange(String key, String value, long offset) {
        redisTemplate.opsForValue().set(key, value, offset);
    }

    /**
     * 获取字符串的长度
     *
     * @param key
     * @return
     */
    public Long size(String key) {
        return redisTemplate.opsForValue().size(key);
    }

    /**
     * 批量添加
     *
     * @param maps
     */
    public void multiSet(Map<String, String> maps) {
        redisTemplate.opsForValue().multiSet(maps);
    }

    /**
     * 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
     *
     * @param maps
     * @return 之前已经存在返回false, 不存在返回true
     */
    public boolean multiSetIfAbsent(Map<String, String> maps) {
        return redisTemplate.opsForValue().multiSetIfAbsent(maps);
    }

    /**
     * 增加(自增长), 负数则为自减
     *
     * @param key
     * @return
     */
    public Long incrBy(String key, long increment) {
        return redisTemplate.opsForValue().increment(key, increment);
    }

    /**
     * @param key
     * @return
     */
    public Double incrByFloat(String key, double increment) {
        return redisTemplate.opsForValue().increment(key, increment);
    }

    /**
     * 追加到末尾
     *
     * @param key
     * @param value
     * @return
     */
    public Integer append(String key, String value) {
        return redisTemplate.opsForValue().append(key, value);
    }

    /** -------------------hash相关操作------------------------- */

    /**
     * 获取存储在哈希表中指定字段的值
     *
     * @param key
     * @param field
     * @return
     */
    public Object hGet(String key, String field) {
        return redisTemplate.opsForHash().get(key, field);
    }

    /**
     * 获取所有给定字段的值
     *
     * @param key
     * @return
     */
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 获取所有给定字段的值
     *
     * @param key
     * @param fields
     * @return
     */
    public List<Object> hMultiGet(String key, Collection<Object> fields) {
        return redisTemplate.opsForHash().multiGet(key, fields);
    }

    public void hPut(String key, String hashKey, String value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    public void hPutAll(String key, Map<String, String> maps) {
        redisTemplate.opsForHash().putAll(key, maps);
    }

    /**
     * 仅当hashKey不存在时才设置
     *
     * @param key
     * @param hashKey
     * @param value
     * @return
     */
    public Boolean hPutIfAbsent(String key, String hashKey, String value) {
        return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
    }

    /**
     * 删除一个或多个哈希表字段
     *
     * @param key
     * @param fields
     * @return
     */
    public Long hDelete(String key, Object... fields) {
        return redisTemplate.opsForHash().delete(key, fields);
    }

    /**
     * 查看哈希表 key 中,指定的字段是否存在
     *
     * @param key
     * @param field
     * @return
     */
    public boolean hExists(String key, String field) {
        return redisTemplate.opsForHash().hasKey(key, field);
    }

    /**
     * 为哈希表 key 中的指定字段的整数值加上增量 increment
     *
     * @param key
     * @param field
     * @param increment
     * @return
     */
    public Long hIncrBy(String key, Object field, long increment) {
        return redisTemplate.opsForHash().increment(key, field, increment);
    }

    /**
     * 为哈希表 key 中的指定字段的整数值加上增量 increment
     *
     * @param key
     * @param field
     * @param delta
     * @return
     */
    public Double hIncrByFloat(String key, Object field, double delta) {
        return redisTemplate.opsForHash().increment(key, field, delta);
    }

    /**
     * 获取所有哈希表中的字段
     *
     * @param key
     * @return
     */
    public Set<Object> hKeys(String key) {
        return redisTemplate.opsForHash().keys(key);
    }

    /**
     * 获取哈希表中字段的数量
     *
     * @param key
     * @return
     */
    public Long hSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    /**
     * 获取哈希表中所有值
     *
     * @param key
     * @return
     */
    public List<Object> hValues(String key) {
        return redisTemplate.opsForHash().values(key);
    }

    /**
     * 迭代哈希表中的键值对
     *
     * @param key
     * @param options
     * @return
     */
    public Cursor<Entry<Object, Object>> hScan(String key, ScanOptions options) {
        return redisTemplate.opsForHash().scan(key, options);
    }

    /** ------------------------list相关操作---------------------------- */

    /**
     * 通过索引获取列表中的元素
     *
     * @param key
     * @param index
     * @return
     */
    public String lIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    /**
     * 获取列表指定范围内的元素
     *
     * @param key
     * @param start 开始位置, 0是开始位置
     * @param end   结束位置, -1返回所有
     * @return
     */
    public List<String> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    /**
     * 存储在list头部
     *
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPush(String key, String value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPushAll(String key, String... value) {
        return redisTemplate.opsForList().leftPushAll(key, value);
    }

    /**
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPushAll(String key, Collection<String> value) {
        return redisTemplate.opsForList().leftPushAll(key, value);
    }

    /**
     * 当list存在的时候才加入
     *
     * @param key
     * @param value
     * @return
     */
    public Long lLeftPushIfPresent(String key, String value) {
        return redisTemplate.opsForList().leftPushIfPresent(key, value);
    }

    /**
     * 如果pivot存在,再pivot前面添加
     *
     * @param key
     * @param pivot
     * @param value
     * @return
     */
    public Long lLeftPush(String key, String pivot, String value) {
        return redisTemplate.opsForList().leftPush(key, pivot, value);
    }

    /**
     * @param key
     * @param value
     * @return
     */
    public Long lRightPush(String key, String value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    /**
     * @param key
     * @param value
     * @return
     */
    public Long lRightPushAll(String key, String... value) {
        return redisTemplate.opsForList().rightPushAll(key, value);
    }

    /**
     * @param key
     * @param value
     * @return
     */
    public Long lRightPushAll(String key, Collection<String> value) {
        return redisTemplate.opsForList().rightPushAll(key, value);
    }

    /**
     * 为已存在的列表添加值
     *
     * @param key
     * @param value
     * @return
     */
    public Long lRightPushIfPresent(String key, String value) {
        return redisTemplate.opsForList().rightPushIfPresent(key, value);
    }

    /**
     * 在pivot元素的右边添加值
     *
     * @param key
     * @param pivot
     * @param value
     * @return
     */
    public Long lRightPush(String key, String pivot, String value) {
        return redisTemplate.opsForList().rightPush(key, pivot, value);
    }

    /**
     * 通过索引设置列表元素的值
     *
     * @param key
     * @param index 位置
     * @param value
     */
    public void lSet(String key, long index, String value) {
        redisTemplate.opsForList().set(key, index, value);
    }

    /**
     * 移出并获取列表的第一个元素
     *
     * @param key
     * @return 删除的元素
     */
    public String lLeftPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
     *
     * @param key
     * @param timeout 等待时间
     * @param unit    时间单位
     * @return
     */
    public String lBLeftPop(String key, long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().leftPop(key, timeout, unit);
    }

    /**
     * 移除并获取列表最后一个元素
     *
     * @param key
     * @return 删除的元素
     */
    public String lRightPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    /**
     * 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
     *
     * @param key
     * @param timeout 等待时间
     * @param unit    时间单位
     * @return
     */
    public String lBRightPop(String key, long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().rightPop(key, timeout, unit);
    }

    /**
     * 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
     *
     * @param sourceKey
     * @param destinationKey
     * @return
     */
    public String lRightPopAndLeftPush(String sourceKey, String destinationKey) {
        return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
                destinationKey);
    }

    /**
     * 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止
     *
     * @param sourceKey
     * @param destinationKey
     * @param timeout
     * @param unit
     * @return
     */
    public String lBRightPopAndLeftPush(String sourceKey, String destinationKey,
                                        long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().rightPopAndLeftPush(sourceKey,
                destinationKey, timeout, unit);
    }

    /**
     * 删除集合中值等于value得元素
     *
     * @param key
     * @param index index=0, 删除所有值等于value的元素; index>0, 从头部开始删除第一个值等于value的元素;
     *              index<0, 从尾部开始删除第一个值等于value的元素;
     * @param value
     * @return
     */
    public Long lRemove(String key, long index, String value) {
        return redisTemplate.opsForList().remove(key, index, value);
    }

    /**
     * 裁剪list
     *
     * @param key
     * @param start
     * @param end
     */
    public void lTrim(String key, long start, long end) {
        redisTemplate.opsForList().trim(key, start, end);
    }

    /**
     * 获取列表长度
     *
     * @param key
     * @return
     */
    public Long lLen(String key) {
        return redisTemplate.opsForList().size(key);
    }

    /** --------------------set相关操作-------------------------- */

    /**
     * set添加元素
     *
     * @param key
     * @param values
     * @return
     */
    public Long sAdd(String key, String... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    /**
     * set移除元素
     *
     * @param key
     * @param values
     * @return
     */
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    /**
     * 移除并返回集合的一个随机元素
     *
     * @param key
     * @return
     */
    public String sPop(String key) {
        return redisTemplate.opsForSet().pop(key);
    }

    /**
     * 将元素value从一个集合移到另一个集合
     *
     * @param key
     * @param value
     * @param destKey
     * @return
     */
    public Boolean sMove(String key, String value, String destKey) {
        return redisTemplate.opsForSet().move(key, value, destKey);
    }

    /**
     * 获取集合的大小
     *
     * @param key
     * @return
     */
    public Long sSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    /**
     * 判断集合是否包含value
     *
     * @param key
     * @param value
     * @return
     */
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    /**
     * 获取两个集合的交集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<String> sIntersect(String key, String otherKey) {
        return redisTemplate.opsForSet().intersect(key, otherKey);
    }

    /**
     * 获取key集合与多个集合的交集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sIntersect(String key, Collection<String> otherKeys) {
        return redisTemplate.opsForSet().intersect(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的交集存储到destKey集合中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long sIntersectAndStore(String key, String otherKey, String destKey) {
        return redisTemplate.opsForSet().intersectAndStore(key, otherKey,
                destKey);
    }

    /**
     * key集合与多个集合的交集存储到destKey集合中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long sIntersectAndStore(String key, Collection<String> otherKeys,
                                   String destKey) {
        return redisTemplate.opsForSet().intersectAndStore(key, otherKeys,
                destKey);
    }

    /**
     * 获取两个集合的并集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sUnion(String key, String otherKeys) {
        return redisTemplate.opsForSet().union(key, otherKeys);
    }

    /**
     * 获取key集合与多个集合的并集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sUnion(String key, Collection<String> otherKeys) {
        return redisTemplate.opsForSet().union(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的并集存储到destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long sUnionAndStore(String key, String otherKey, String destKey) {
        return redisTemplate.opsForSet().unionAndStore(key, otherKey, destKey);
    }

    /**
     * key集合与多个集合的并集存储到destKey中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long sUnionAndStore(String key, Collection<String> otherKeys,
                               String destKey) {
        return redisTemplate.opsForSet().unionAndStore(key, otherKeys, destKey);
    }

    /**
     * 获取两个集合的差集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<String> sDifference(String key, String otherKey) {
        return redisTemplate.opsForSet().difference(key, otherKey);
    }

    /**
     * 获取key集合与多个集合的差集
     *
     * @param key
     * @param otherKeys
     * @return
     */
    public Set<String> sDifference(String key, Collection<String> otherKeys) {
        return redisTemplate.opsForSet().difference(key, otherKeys);
    }

    /**
     * key集合与otherKey集合的差集存储到destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long sDifference(String key, String otherKey, String destKey) {
        return redisTemplate.opsForSet().differenceAndStore(key, otherKey,
                destKey);
    }

    /**
     * key集合与多个集合的差集存储到destKey中
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long sDifference(String key, Collection<String> otherKeys,
                            String destKey) {
        return redisTemplate.opsForSet().differenceAndStore(key, otherKeys,
                destKey);
    }

    /**
     * 获取集合所有元素
     *
     * @param key
     * @return
     */
    public Set<String> setMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 随机获取集合中的一个元素
     *
     * @param key
     * @return
     */
    public String sRandomMember(String key) {
        return redisTemplate.opsForSet().randomMember(key);
    }

    /**
     * 随机获取集合中count个元素
     *
     * @param key
     * @param count
     * @return
     */
    public List<String> sRandomMembers(String key, long count) {
        return redisTemplate.opsForSet().randomMembers(key, count);
    }

    /**
     * 随机获取集合中count个元素并且去除重复的
     *
     * @param key
     * @param count
     * @return
     */
    public Set<String> sDistinctRandomMembers(String key, long count) {
        return redisTemplate.opsForSet().distinctRandomMembers(key, count);
    }

    /**
     * @param key
     * @param options
     * @return
     */
    public Cursor<String> sScan(String key, ScanOptions options) {
        return redisTemplate.opsForSet().scan(key, options);
    }

    /**------------------zSet相关操作--------------------------------*/

    /**
     * 添加元素,有序集合是按照元素的score值由小到大排列
     *
     * @param key
     * @param value
     * @param score
     * @return
     */
    public Boolean zAdd(String key, String value, double score) {
        return redisTemplate.opsForZSet().add(key, value, score);
    }

    /**
     * 元素值按维度自增
     *
     * @param key
     * @param value
     * @param score
     * @return
     */
    public Double zAddForIncre(String key, String value, double score) {
        return redisTemplate.opsForZSet().incrementScore(key, value, score);
    }

    /**
     * @param key
     * @param values
     * @return
     */
    public Long zAdd(String key, Set<TypedTuple<String>> values) {
        return redisTemplate.opsForZSet().add(key, values);
    }

    /**
     * @param key
     * @param values
     * @return
     */
    public Long zRemove(String key, Object... values) {
        return redisTemplate.opsForZSet().remove(key, values);
    }

    /**
     * 增加元素的score值,并返回增加后的值
     *
     * @param key
     * @param value
     * @param delta
     * @return
     */
    public Double zIncrementScore(String key, String value, double delta) {
        return redisTemplate.opsForZSet().incrementScore(key, value, delta);
    }

    /**
     * 返回元素在集合的排名,有序集合是按照元素的score值由小到大排列
     *
     * @param key
     * @param value
     * @return 0表示第一位
     */
    public Long zRank(String key, Object value) {
        return redisTemplate.opsForZSet().rank(key, value);
    }

    /**
     * 返回元素在集合的排名,按元素的score值由大到小排列
     *
     * @param key
     * @param value
     * @return
     */
    public Long zReverseRank(String key, Object value) {
        return redisTemplate.opsForZSet().reverseRank(key, value);
    }

    /**
     * 获取集合的元素, 从小到大排序
     *
     * @param key
     * @param start 开始位置
     * @param end   结束位置, -1查询所有
     * @return
     */
    public Set<String> zRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().range(key, start, end);
    }

    /**
     * 获取集合元素, 并且把score值也获取
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<TypedTuple<String>> zRangeWithScores(String key, long start,
                                                    long end) {
        return redisTemplate.opsForZSet().rangeWithScores(key, start, end);
    }

    /**
     * 根据Score值查询集合元素
     *
     * @param key
     * @param min 最小值
     * @param max 最大值
     * @return
     */
    public Set<String> zRangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(key, min, max);
    }

//    public Set hSet(String key, String valueName, Objects value) {
//        return redisTemplate.opsForHash().put(key,valueName,value);
//    }

    /**
     * 根据Score值查询集合元素, 从小到大排序
     *
     * @param key
     * @param min 最小值
     * @param max 最大值
     * @return
     */
    public Set<TypedTuple<String>> zRangeByScoreWithScores(String key,
                                                           double min, double max) {
        return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max);
    }

    /**
     * @param key
     * @param min
     * @param max
     * @param start
     * @param end
     * @return
     */
    public Set<TypedTuple<String>> zRangeByScoreWithScores(String key,
                                                           double min, double max, long start, long end) {
        return redisTemplate.opsForZSet().rangeByScoreWithScores(key, min, max,
                start, end);
    }

    /**
     * 获取集合的元素, 从大到小排序
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<String> zReverseRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    /**
     * 获取集合的元素, 从大到小排序, 并返回score值
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<TypedTuple<String>> zReverseRangeWithScores(String key,
                                                           long start, long end) {
        return redisTemplate.opsForZSet().reverseRangeWithScores(key, start,
                end);
    }

    /**
     * 根据Score值查询集合元素, 从大到小排序
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<String> zReverseRangeByScore(String key, double min,
                                            double max) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
    }

    /**
     * 根据Score值查询集合元素, 从大到小排序
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<TypedTuple<String>> zReverseRangeByScoreWithScores(
            String key, double min, double max) {
        return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key,
                min, max);
    }

    /**
     * @param key
     * @param min
     * @param max
     * @param start
     * @param end
     * @return
     */
    public Set<String> zReverseRangeByScore(String key, double min,
                                            double max, long start, long end) {
        return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max,
                start, end);
    }

    /**
     * 根据score值获取集合元素数量
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Long zCount(String key, double min, double max) {
        return redisTemplate.opsForZSet().count(key, min, max);
    }

    /**
     * 获取集合大小
     *
     * @param key
     * @return
     */
    public Long zSize(String key) {
        return redisTemplate.opsForZSet().size(key);
    }

    /**
     * 获取集合大小
     *
     * @param key
     * @return
     */
    public Long zZCard(String key) {
        return redisTemplate.opsForZSet().zCard(key);
    }

    /**
     * 获取集合中value元素的score值
     *
     * @param key
     * @param value
     * @return
     */
    public Double zScore(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 移除指定索引位置的成员
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Long zRemoveRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().removeRange(key, start, end);
    }

    /**
     * 根据指定的score值的范围来移除成员
     *
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Long zRemoveRangeByScore(String key, double min, double max) {
        return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
    }

    /**
     * 获取key和otherKey的并集并存储在destKey中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zUnionAndStore(String key, String otherKey, String destKey) {
        return redisTemplate.opsForZSet().unionAndStore(key, otherKey, destKey);
    }

    /**
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long zUnionAndStore(String key, Collection<String> otherKeys,
                               String destKey) {
        return redisTemplate.opsForZSet()
                .unionAndStore(key, otherKeys, destKey);
    }

    /**
     * 交集
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zIntersectAndStore(String key, String otherKey,
                                   String destKey) {
        return redisTemplate.opsForZSet().intersectAndStore(key, otherKey,
                destKey);
    }

    /**
     * 交集
     *
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long zIntersectAndStore(String key, Collection<String> otherKeys,
                                   String destKey) {
        return redisTemplate.opsForZSet().intersectAndStore(key, otherKeys,
                destKey);
    }

    /**
     * @param key
     * @param options
     * @return
     */
    public Cursor<TypedTuple<String>> zScan(String key, ScanOptions options) {
        return redisTemplate.opsForZSet().scan(key, options);
    }

    /** -------------------锁相关操作--------------------- */

    /**
     * 尝试获取锁
     *
     * @param key
     * @param timeout 秒为单位
     * @return
     */
    public boolean tryLock(String key, long timeout) {
        if (setIfAbsent(key, "1")) {
            expire(key, timeout, TimeUnit.SECONDS);
            return true;
        } else {
            return false;
        }
    }

    /**
     * 释放锁
     * @param key
     * @return
     */
    public boolean unlock(String key) {
        delete(key);
        return true;
    }
}

UserUtil.java

package org.lgx.bluegrass.bluegrassgateway.util;

import lombok.extern.slf4j.Slf4j;
import org.lgx.bluegrass.api.constant.ConstantApi;
import org.lgx.bluegrass.api.constant.RedisKeyConstant;
import org.lgx.bluegrass.api.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;

/**
 * @Description TODO
 * @Date 2021/11/26 15:14
 * @Author lgx
 * @Version 1.0
 */
@Slf4j
@Component
public class UserUtil {

    public static RedisUtil redisUtilStatic;
    @Autowired
    private RedisUtil redisUtil;

    @PostConstruct
    public void init() {
        redisUtilStatic = redisUtil;
    }

    /**
     * 单位秒
     */
    public static final long EXPIRE_TIME = 3600 * 12;

    public static final String CODE_SUFFIX = "-code";
    /**
     * 获取当前登录用户id
     *
     * @param request
     * @return
     */
    public static String getUserId(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(ConstantApi.X_AUTHORIZATION);
        return getUid(token);
    }

    /**
     * 根据token 获取用户id
     *
     * @param token
     * @return
     */
    private static String getUid(String token) {
        String uid = JwtUtil.getUID(token);
        if (StringUtils.isEmpty(uid)) {
            return null;
        }
        String token_set = redisUtilStatic.get(uid);
        if (StringUtils.isEmpty(token_set) || !token_set.equals(token)) {
            return null;
        }
        return uid;
    }
    /**
     * 设置当前用户id
     *
     * @param userId
     */
    public static String setUser(String userId, String code) {
        String sessionToken = setUserId(userId, code);
        return sessionToken;
    }
    /**
     * 设置useid
     *
     * @param userId
     * @param code
     * @return
     */
    private static String setUserId(String userId, String code) {
        if (StringUtils.isEmpty(userId)) {
            return "";
        }
        String sessionToken = redisUtilStatic.get(userId);
        if (StringUtils.isEmpty(sessionToken)) {
            // 生成token
            sessionToken = JwtUtil.createToken(userId);
        }
        redisUtilStatic.set(userId, sessionToken, EXPIRE_TIME);
        redisUtilStatic.set(userId + CODE_SUFFIX, code, EXPIRE_TIME);
        //清空数据权限缓存
        String redisKey = RedisKeyConstant.USER_DATA_PREFIX + userId;
        if (redisUtilStatic.hasKey(redisKey)) {
            redisUtilStatic.delete(redisKey);
        }
        return sessionToken;
    }
}

(3.2.2) 增加 TokenCheckFilter.java 过滤器

package org.lgx.bluegrass.bluegrassgateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.lgx.bluegrass.api.util.UrlPathUtil;
import org.lgx.bluegrass.bluegrassgateway.util.GatewayUtil;
import org.lgx.bluegrass.bluegrassgateway.util.UserUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;

/**
 * @Description TODO
 * @Date 2021/11/26 15:10
 * @Author lgx
 * @Version 1.0
 */
@Slf4j
@RefreshScope
public class TokenCheckFilter  implements GlobalFilter, Ordered {
    // 需要跳过的路径
    @Value("${token.skip-urls-pattern:abcdefg}")
    private String skipUrlsPattern;
    // 是否启用
    @Value("${token.enable:true}")
    private Boolean tokenEnable;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        log.info(" start in gateway TokenCheckFilter  header Authorization {}", request.getHeaders().getFirst("X-Authorization"));
        URI uri = request.getURI();
        log.info("in gateway,token skip url pattern is {}", skipUrlsPattern);
        if (UrlPathUtil.isUrlPathMatch(uri.getPath(), skipUrlsPattern, false)) {
            log.info("in gateway TokenCheckFilter,skip token auth");
            return chain.filter(exchange);
        }
        if (tokenEnable) {
            log.info("in gateway TokenCheckFilter,open token auth");
            ServerHttpResponse serverHttpResponse = exchange.getResponse();
            String userId = UserUtil.getUserId(request);
            if (userId == null) {
                log.info("in gateway TokenCheckFilter,token error");
                return GatewayUtil.responseErrorStatus(HttpStatus.UNAUTHORIZED, serverHttpResponse, 401, "token验证失败,token不存在");
            }
        } else {
            log.info("in gateway TokenCheckFilter,change token for test");
            //TODO 测试代码 需要删除 如果没传递token ---默认用户
            String userId = UserUtil.getUserId(request);
            if (null == userId) {
                String sessionToken = UserUtil.setUser("000015113","test_code");
                ServerHttpRequest host = request.mutate().header("X-Authorization", sessionToken).build();
                ServerWebExchange build = exchange.mutate().request(host).build();
                log.info("in gateway TokenCheckFilter,Authorization has been change {}", sessionToken);
                return chain.filter(build);
            }
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

在另外的微服务中增加用户的登录并且保存用户登录态到redis
在这里插入图片描述
CorsConfig.java 增加token验证过滤器:

/**
     * token 验证
     * @return
     */
    @Bean
    @RefreshScope
    public TokenCheckFilter tokenCheckFilter() {
        return new TokenCheckFilter();
    }

(3.2.3) token 测试:
(3.2.3.1)返回的accessToken后续的访问在header 中增加X-Authorization参数即可
Idea+maven+spring-cloud项目搭建系列--2.1gateway网关路由,鉴权,日志记录_第7张图片
3.3 增加全局接口访问记录过滤器,并将访问记录表按月分表:
(3.3.1)网关增加请求参数追加过滤器:RequestFilter.java

package org.lgx.bluegrass.bluegrassgateway.filter;


import lombok.extern.slf4j.Slf4j;
import org.lgx.bluegrass.api.constant.ConstantApi;
import org.lgx.bluegrass.bluegrassgateway.util.GatewayUtil;
import org.lgx.bluegrass.bluegrassgateway.util.UserUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;

/**
 * @Description TODO
 * @Date 2021/4/21 14:17
 * @Author lgx
 * @Version 1.0
 */
@Slf4j
public class RequestFilter implements GlobalFilter, Ordered {
    @Value("${log_domain.address:RequestFilter/}")
    private String serverAddress;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        log.info(" start in gateway RequestFilter  header Authorization {}", serverHttpRequest.getHeaders().getFirst(ConstantApi.X_AUTHORIZATION));
        URI uri = exchange.getRequest().getURI();
        StringBuilder query = new StringBuilder();
        String originalQuery = uri.getRawQuery();

        if (StringUtils.hasText(originalQuery)) {
            query.append(originalQuery);
            if (originalQuery.charAt(originalQuery.length() - 1) != '&') {
                query.append('&');
            }
        }
        String ip = GatewayUtil.getIpAddressAll(serverHttpRequest);
        String url = serverHttpRequest.getURI().getPath();
        if (url.startsWith("/")) {
            url = serverAddress + url.substring(1);
        } else {
            url = serverAddress + url;
        }
        //TODO urlencode?
        query.append("operationClientIp");
        query.append('=');
        query.append((ip == null || "0:0:0:0:0:0:0:1".equals(ip)) ? "110.110.110.110" : ip);

        query.append("&operationRequestUrl");
        query.append('=');
        query.append(url);

        query.append("&operationUserId");
        query.append('=');
        String userId = UserUtil.getUserId(serverHttpRequest);
        query.append(userId != null ? userId : "");

        try {
            URI newUri = UriComponentsBuilder.fromUri(uri)
                    .replaceQuery(query.toString())
                    .build(true)
                    .toUri();

            ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
            log.info(" end in gateway RequestFilter  header Authorization {}", request.getHeaders().getFirst(ConstantApi.X_AUTHORIZATION));
            return chain.filter(exchange.mutate().request(request).build());

        } catch (RuntimeException ex) {
            log.error("Invalid URI query:{}", query.toString());
            throw new IllegalStateException("Invalid URI query: \"" + query.toString() + "\"");
        }

    }

    @Override
    public int getOrder() {
        return 0;
    }
}

(3.3.2)增加日志处理aop 切面
(3.3.2.1)微服务中增加aop jar 依赖:

<!-- -boot-starter依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

(3.3.2.2)api 公共模块定义切点,及增加公用枚举类:
切面定义类:OperationLogAnnotation.java

package org.lgx.bluegrass.api.annotation;

import org.lgx.bluegrass.api.enums.ModuleEnum;
import org.lgx.bluegrass.api.enums.OperationEnum;

import java.lang.annotation.*;

/**
 * @Description TODO
 * @Date 2021/3/17 15:45
 * @Author lgx
 * @Version 1.0
 */
//注解放置的目标位置即方法级别
@Target(ElementType.METHOD)
//注解在哪个阶段执行
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLogAnnotation {

    String operTitle() default ""; // 操作标题

    String operContent() default "";  // 操作内容

    String moduleId() default "";  // 操作模块id

    OperationEnum operationType() default OperationEnum.OTHER; // 操作code

    ModuleEnum moduleCode() default  ModuleEnum.其他;

}

定义枚举公共类:LogFiledEnum.java

package org.lgx.bluegrass.api.enums;

/**
 * @Description TODO
 * @Date 2021/3/18 14:49
 * @Author lgx
 * @Version 1.0
 */

public enum LogFiledEnum {
    用户ID("operationUserId", "用户ID"),
    用户名("operationUserName", "用户名"),
    模块ID("operationModuleId", "模块ID"),
    模块CODE("operationModuleCode", "模块CODE"),
    客户端IP("operationClientIp", "IP"),
    接口地址("operationRequestUrl", "URL"),
    操作("operationType", "CODE"),
    标题("operationTitle", "标题"),
    内容("operationContent", "内容"),
    ;
    private String fileName;
    private String fileId;

    public String getFileName() {
        return fileName;
    }

    public String getFileId() {
        return fileId;
    }


    LogFiledEnum(String fileId, String fileName){
        this.fileId = fileId;
        this.fileName = fileName;
    }
    public static LogFiledEnum getByFiledName(String fileName) {
       LogFiledEnum result = null;
        for (LogFiledEnum filed:LogFiledEnum.values()) {
            if (fileName.equals(filed.getFileName()) ) {
                result = filed;
                break;
            }
        }
        return result;
    }
}

模块枚举类:ModuleEnum.java

package org.lgx.bluegrass.api.enums;

/**
 * @Description TODO
 * @Date 2021/6/17 10:50
 * @Author lgx
 * @Version 1.0
 */
public enum ModuleEnum {
    其他("其他","other"),
    首页("首页","home"),
    ;
    private String moduleName;
    private String moduleCode;


    ModuleEnum(String moduleName, String moduleCode) {
        this.moduleName = moduleName;
        this.moduleCode = moduleCode;
    }

    public String getModuleCode() {
        return moduleCode;
    }
    public String getModuleName() {
        return moduleName;
    }

}

操作枚举类OperationCodeEnum.java

package org.lgx.bluegrass.api.enums;

/**
 * @Description TODO
 * @Date 2021/3/30 19:46
 * @Author lgx
 * @Version 1.0
 */
public enum OperationCodeEnum {
    新增("ADD", 1),
    删除("DELETE", -1),
    查询("QUERY", 0),
    修改("EDIT", 2),
    导入("IMPORT", 3),
    导出("EXPORT", 4),
    下载("DOWNLOAD", 5),
    上传("UPLOAD", 6),
    数据传输("SENDDATA", 7),
    其它("OTHER", -2),
    ;
    private String operationType;
    private Integer operationTypeCode;

    OperationCodeEnum(String operationType, Integer operationTypeCode) {
        this.operationType = operationType;
        this.operationTypeCode = operationTypeCode;
    }

    public String getOperationType() {
        return operationType;
    }

    public Integer getOperationTypeCode() {
        return operationTypeCode;
    }

    public static OperationCodeEnum getByoperationType(String operationType) {
        OperationCodeEnum result = null;
        for (OperationCodeEnum type :OperationCodeEnum.values()) {
            if (operationType.equals(type.getOperationType())) {
                result = type;
                break;
            }
        }
        return result;
    }

}

OperationEnum.java

package org.lgx.bluegrass.api.enums;

/**
 * @Description TODO
 * @Date 2021/3/30 19:23
 * @Author lgx
 * @Version 1.0
 */
public enum OperationEnum {
    /**
     * 添加操作
     */
    ADD
    /** 修改 */
    , EDIT
    /** 删除 */
    , DELETE
    /** 查询 */
    , QUERY
    /** 导入 */
    , IMPORT
    /** 导出 */
    , EXPORT
    /** 下载 */
    , UPLOAD
    /** 远程数据传输 */
    , SENDDATA
    /** 下载 */
    , DOWNLOAD
    /** 页面 */
    , PAGE
    /** 移动端H5页面 */
    , MOBILEPAGE
    /** 其它 */
    , OTHER
}

(3.3.2.3)微服务中定义切面日志处理类:OperationLogAspect.java

package org.lgx.bluegrass.bluegrasscoree.aspect;


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.lgx.bluegrass.api.annotation.OperationLogAnnotation;
import org.lgx.bluegrass.api.dto.UserOperationLogDto;
import org.lgx.bluegrass.api.enums.LogFiledEnum;
import org.lgx.bluegrass.api.enums.OperationCodeEnum;
import org.lgx.bluegrass.api.util.ExceptionFormatUtil;
import org.lgx.bluegrass.bluegrasscoree.service.userlog.ITUserOpLogService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @Description TODO
 * @Date 2021/3/17 15:50
 * @Author lgx
 * @Version 1.0
 */
@Aspect
@Component
@Slf4j
public class OperationLogAspect implements Ordered {

    private static ExecutorService executors = Executors.newFixedThreadPool(10);
    @Autowired
    private ITUserOpLogService userLogService;

    private static StringBuffer stringBuffer = new StringBuffer();

    /**
     * 设置操作日志切入点   在注解的位置切入代码
     */
    @Pointcut("@annotation(org.lgx.bluegrass.api.annotation.OperationLogAnnotation)")
    public void operLogPoinCut() {

    }

    @Around("operLogPoinCut()")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        Map<String, Object> data = new HashMap<>();

        data.put(LogFiledEnum.用户ID.getFileId(), request.getParameter(LogFiledEnum.用户ID.getFileId()));
        data.put(LogFiledEnum.用户名.getFileId(), request.getParameter(LogFiledEnum.用户名.getFileId()));
        data.put(LogFiledEnum.客户端IP.getFileId(), request.getParameter(LogFiledEnum.客户端IP.getFileId()));
        data.put(LogFiledEnum.接口地址.getFileId(), request.getParameter(LogFiledEnum.接口地址.getFileId()));

        int isSuccess = 0;
        Exception exception = null;
        try {
            result = joinPoint.proceed();
        } catch (Exception e) {
            e.printStackTrace();
            exception =e;
            log.error("OperationLogAspect is get method error,cause:{}", ExceptionFormatUtil.buildErrorMessage(e));
            isSuccess = 1;
        } finally {
            //create log object
            OperationLogAnnotation annotation;
            Class<?> aClass = joinPoint.getTarget().getClass();
            String name = joinPoint.getSignature().getName();
            List<Method> methods = Arrays.stream(aClass.getMethods()).filter(m -> m.getName().equals(name)).collect(Collectors.toList());
            //If the method name is unique, take the annotation of the current instance
            if (1 == methods.size()) {
                annotation = methods.stream().findFirst().get().getAnnotation(OperationLogAnnotation.class);
            }else {
                //Otherwise, take the default setting annotation of the entry point
                MethodSignature signature = (MethodSignature) joinPoint.getSignature();
                annotation = signature.getMethod().getAnnotation(OperationLogAnnotation.class);
            }
            data.put(LogFiledEnum.模块ID.getFileId(), annotation.moduleId());
            data.put(LogFiledEnum.模块CODE.getFileId(), annotation.moduleCode().getModuleCode());
            data.put(LogFiledEnum.内容.getFileId(), annotation.operContent());
            data.put(LogFiledEnum.标题.getFileId(), annotation.operTitle());
            data.put(LogFiledEnum.操作.getFileId(), OperationCodeEnum.getByoperationType(annotation.operationType().name()).getOperationTypeCode());
            UserOperationLogDto logDto = null;
            try {
                logDto = returnLog(data);
            } catch (Exception e) {
                log.error("日志记录异常{}", ExceptionFormatUtil.buildErrorMessage(e));
            }
            if (logDto != null) {
                logDto.setOperationIsSuccess(isSuccess);
                // feign 调用存储日志
                UserOperationLogDto finalLogDto = logDto;
                CompletableFuture.runAsync(() -> {
                    RequestContextHolder.setRequestAttributes(requestAttributes);
                    UserOperationLogDto log = new UserOperationLogDto();
                    BeanUtils.copyProperties(finalLogDto, log);
                    userLogService.saveLog(log);
                }, executors);
            }
            if (exception!= null){
                throw exception;
            }
        }
        return result;
    }

    public UserOperationLogDto returnLog(Map<String, Object> data) {
        UserOperationLogDto logDto = new UserOperationLogDto();

        logDto.setOperationSource(1);
        logDto.setOperationLogType(0);
        logDto.setOperationUserId(data.get(LogFiledEnum.用户ID.getFileId()) + "");
        logDto.setOperationUserName(data.get(LogFiledEnum.用户名.getFileId()) + "");
        logDto.setOperationModuleId(data.get(LogFiledEnum.模块ID.getFileId()) + "");
        logDto.setOperationModuleCode(data.get(LogFiledEnum.模块CODE.getFileId()) + "");
        logDto.setOperationClientIp(data.get(LogFiledEnum.客户端IP.getFileId()) + "");
        logDto.setOperationRequestUrl(data.get(LogFiledEnum.接口地址.getFileId()) + "");
        logDto.setOperationModuleId(data.get(LogFiledEnum.模块ID.getFileId()) + "");
        logDto.setOperationTitle(data.get(LogFiledEnum.标题.getFileId()) + "");
        logDto.setOperationContent(data.get(LogFiledEnum.内容.getFileId()) + "");
        logDto.setOperationType(Integer.parseInt(data.get(LogFiledEnum.操作.getFileId()) + ""));
        return logDto;

    }

    @Override
    public int getOrder() {
        return 0;
    }
}

(3.3.2.4)增加日志保存业务类:TUserOpLogEntity.java

package org.lgx.bluegrass.bluegrasscoree.entity.userlog;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
* 

* 操作日志 *

* * @author lgx * @since 2021-03-18 */
@Data @TableName("t_user_op_log") public class TUserOpLogEntity implements Serializable { private static final long serialVersionUID = 1L; /** * 用户id */ private String userId; /** * 用户名 */ private String userName; /** * 标题 */ private String title; /** * 模块id */ private String moduleId; /** * 模块名称 */ private String moduleName; /** * 模块code */ private String moduleCode; /** * 0.操作日志 1.系统日志 */ private Integer logType=0; /** * 客户端ip */ private String clientIp; /** * 请求URL */ private String requestUrl; /** * 查询:0 增加:1 删除:-1 修改:2 */ private Integer operationType; /** * 操作内容 */ private String operationContent; /** * 1 PC端 ,2移动端, 3系统 */ private Integer source =1; /** * 0:成功 1:失败 */ private Integer success =0; /** * 是否删除 */ private Integer isDelete =0; /** * 创建人 */ @TableField(fill = FieldFill.INSERT) private String createdBy; /** * 最后更新人 */ @TableField(fill = FieldFill.INSERT_UPDATE) private String updatedBy; /** * 创建时间 */ private Date createdTime; /** * 更新时间 */ private Date updatedTime; }

(3.3.2.5)日志保存业务类:
ITUserOpLogService.java

package org.lgx.bluegrass.bluegrasscoree.service.userlog;

import org.lgx.bluegrass.api.dto.UserOperationLogDto;

import java.util.Map;

/**
 * @Description TODO
 * @Date 2021/11/29 09:53
 * @Author lgx
 * @Version 1.0
 */
public interface ITUserOpLogService {
    Map<String,Object> saveLog(UserOperationLogDto userSubArticleDto);
}

ITUserOpLogServiceImpl.java

package org.lgx.bluegrass.bluegrasscoree.serviceimp.userlog;

import lombok.extern.slf4j.Slf4j;
import org.lgx.bluegrass.api.dto.UserOperationLogDto;
import org.lgx.bluegrass.api.util.ExceptionFormatUtil;
import org.lgx.bluegrass.bluegrasscoree.entity.userlog.TUserOpLogEntity;
import org.lgx.bluegrass.bluegrasscoree.mapper.userlog.TUserOpLogMapper;
import org.lgx.bluegrass.bluegrasscoree.service.common.CommonService;
import org.lgx.bluegrass.bluegrasscoree.service.userlog.ITUserOpLogService;
import org.lgx.bluegrass.bluegrasscoree.util.RedisUtil;
import org.lgx.bluegrass.bluegrasscoree.util.UserUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

/**
 * @Description TODO
 * @Date 2021/11/29 09:54
 * @Author lgx
 * @Version 1.0
 */
@Slf4j
@Service
public class ITUserOpLogServiceImpl implements ITUserOpLogService {
    @Resource
    private RedisUtil redisUtil;
    @Autowired
    private TUserOpLogMapper tUserOpLogMapper;
    @Resource
    private CommonService commonService;

    @Override
    public Map<String, Object> saveLog(UserOperationLogDto userOperationLogDto) {
        Map<String, Object> result = commonService.initSuccessMap();
        try {
            TUserOpLogEntity logEntity = new TUserOpLogEntity();
            logEntity.setUserId(userOperationLogDto.getOperationUserId());
            logEntity.setTitle(userOperationLogDto.getOperationTitle());
            logEntity.setModuleId(userOperationLogDto.getOperationModuleId());
            logEntity.setModuleCode(userOperationLogDto.getOperationModuleCode());
            logEntity.setLogType(userOperationLogDto.getOperationLogType());
            logEntity.setClientIp(userOperationLogDto.getOperationClientIp());
            logEntity.setRequestUrl(userOperationLogDto.getOperationRequestUrl());
            logEntity.setOperationType(userOperationLogDto.getOperationType());
            logEntity.setOperationContent(userOperationLogDto.getOperationContent());
            logEntity.setSource(userOperationLogDto.getOperationSource());
            logEntity.setSuccess(userOperationLogDto.getOperationIsSuccess());
            logEntity.setCreatedBy(UserUtil.getUserId());
            logEntity.setUpdatedBy(UserUtil.getUserId());

            saveOneLog(logEntity);
        } catch (Exception ex) {
            log.error("save log error,cause:{}", ExceptionFormatUtil.buildErrorMessage(ex));
            result.put("success", false);
            result.put("code", 500);
            result.put("errorMsg", ExceptionFormatUtil.buildErrorMessage(ex));
            return result;
        }
        return result;
    }

    private boolean saveOneLog(TUserOpLogEntity logEntity) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMM");
        String nowMonth = dateFormat.format(new Date());
        String key = String.format("cs:system:tuserlog:%s:string", nowMonth);
        log.info("接口调用日志表Redis Key:" + key);
        String tableName = redisUtil.get(key);
        log.info("接口调用日志表表名:" + tableName);
        if (tableName != null) {
            return tUserOpLogMapper.saveOneLog(tableName, logEntity) == 1;
        }

        String newTableName = String.format("t_user_op_log_%s", nowMonth);
        try {
            if (redisUtil.tryLock(newTableName, 10)) {
                log.info("获得Redis锁");
                // 第二层判断,防范作用
                tableName = redisUtil.get(key);
                log.info("再次获取接口调用日志表表名:" + tableName);
                if (tableName != null) {
                    return tUserOpLogMapper.saveOneLog(tableName, logEntity) == 1;
                }

                Map<String, String> list = tUserOpLogMapper.checkTableExistsWithShow(newTableName);
                if (CollectionUtils.isEmpty(list)) {
                    log.info("创建接口调用日志表,并将表名保存到Redis");
                    tUserOpLogMapper.createTable(newTableName);
                    redisUtil.set(key, newTableName);
                }
                return tUserOpLogMapper.saveOneLog(newTableName, logEntity) == 1;
            }
        } catch (Exception e) {
            log.error("新增调用接口日志时获取Redis锁失败_" + e.getMessage(), e);
        } finally {
            redisUtil.unlock(newTableName);
            log.info("释放Redis锁");
        }
        return false;
    }
}

TUserOpLogMapper.java

package org.lgx.bluegrass.bluegrasscoree.mapper.userlog;

import org.apache.ibatis.annotations.Param;
import org.lgx.bluegrass.bluegrasscoree.entity.userlog.TUserOpLogEntity;
import org.springframework.stereotype.Repository;

import java.util.Map;

/**
 * @Description TODO
 * @Date 2021/11/29 09:57
 * @Author lgx
 * @Version 1.0
 */
@Repository
public interface TUserOpLogMapper {
    /**
     * 使用show tables检查表是否存在
     * @param tableName
     * @return
     */
    Map<String, String> checkTableExistsWithShow(@Param("tableName")String tableName);
    /**
     * 创建表
     *
     * @param tableName 表名
     */
    void createTable(@Param("tableName") String tableName);

    /**
     * 新增
     *
     * @param tableName 表名
     * @param userOpLogEntity 调用接口日志数据对象
     * @return 新增成功的记录数
     */
    int saveOneLog(@Param("tableName") String tableName,
                   @Param("userOpLogEntity") TUserOpLogEntity userOpLogEntity);
}

TUserOpLogMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.lgx.bluegrass.bluegrasscoree.mapper.userlog.TUserOpLogMapper">

    <select id="checkTableExistsWithShow"  resultType="java.util.Map">
        show tables like #{tableName}
    </select>
    <insert id="saveOneLog" parameterType="org.lgx.bluegrass.bluegrasscoree.entity.userlog.TUserOpLogEntity">
            INSERT INTO ${tableName} (
                `user_id`,
                `user_name`,
                `title`,
                `module_id`,
                `module_name`,
                `module_code`,
                `log_type`,
                `client_ip`,
                `request_url`,
                `operation_type`,
                `operation_content`,
                `source`,
                `success`,
                `is_delete`,
                `created_by`,
                `updated_by`
            )
            VALUES
                (
                      #{userOpLogEntity.userId},
                      #{userOpLogEntity.userName},
                      #{userOpLogEntity.title},
                      #{userOpLogEntity.moduleId},
                      #{userOpLogEntity.moduleName},
                      #{userOpLogEntity.moduleCode},
                      #{userOpLogEntity.logType},
                      #{userOpLogEntity.clientIp},
                      #{userOpLogEntity.requestUrl},
                      #{userOpLogEntity.operationType},
                      #{userOpLogEntity.operationContent},
                      #{userOpLogEntity.source},
                      #{userOpLogEntity.success},
                      #{userOpLogEntity.isDelete},
                      #{userOpLogEntity.createdBy},
                      #{userOpLogEntity.updatedBy}
                )

    </insert>
    <update id="createTable" parameterType="string" >
        CREATE TABLE ${tableName} (
          `id` bigint(18) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
          `user_id` varchar(35) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户id',
          `user_name` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户名',
          `title` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '标题',
          `module_id` varchar(35) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '模块id',
          `module_name` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '模块名称',
          `module_code` varchar(200) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '模块code',
          `log_type` tinyint(4) NOT NULL DEFAULT '-1' COMMENT '0.操作日志 1.系统日志',
          `client_ip` varchar(255) COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '客户端ip',
          `request_url` varchar(300) COLLATE utf8mb4_bin NOT NULL DEFAULT '' COMMENT '请求URL',
          `operation_type` tinyint(4) NOT NULL DEFAULT '-2' COMMENT '查询:0 增加:1 删除:-1  修改:2',
          `operation_content` longtext COLLATE utf8mb4_bin  COMMENT '操作内容',
          `source` tinyint(4) NOT NULL DEFAULT '-1' COMMENT '1 PC端 ,2移动端, 3系统',
          `success` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '0:成功 1:失败 ',
          `is_delete` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否删除    ',
          `created_by` varchar(40) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建人员',
          `updated_by` varchar(40) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '更新人员',
          `created_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
          `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
          PRIMARY KEY (`id`)
        ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='操作日志';

    </update>
</mapper>

CorsConfig.java 增加全局日志参数过滤器:

/**
     * 请求记录
     * @return
     */
    @Bean
    @RefreshScope
    public RequestFilter requestFilter() {
        return new RequestFilter();
    }

本文参考:
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#configuring-route-predicate-factories-and-gateway-filter-factories

你可能感兴趣的:(#,spring-cloud,Spring框架篇,#,spring-boot,前端,spring,websocket)