zhibing.pu
2024-08-19 b9522d131e168a92c88f23e2c0a3a2df079926b4
ruoyi-gateway/src/main/java/com/ruoyi/gateway/filter/SignFilter.java
@@ -40,155 +40,149 @@
/**
 * 网关鉴权
 *
 *
 * @author ruoyi
 */
@Component
public class SignFilter implements GlobalFilter, Ordered {
    private static final Logger log = LoggerFactory.getLogger(SignFilter.class);
    // 排除过滤的 uri 地址,nacos自行添加
    @Autowired
    private IgnoreWhiteProperties ignoreWhite;
    @Autowired
    private RedisService redisService;
    @Value("${security.sign}")
    private boolean parameter_signature;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpRequest.Builder mutate = request.mutate();
        HttpMethod method = request.getMethod();
        if(method != HttpMethod.POST){
            return chain.filter(exchange.mutate().request(mutate.build()).build());
        }
        String sign = request.getHeaders().getFirst(TokenConstants.SIGN);
        String nonce_str = request.getHeaders().getFirst(TokenConstants.NONCE_STR);
        if (parameter_signature && StringUtils.isEmpty(sign)) {
            return unauthorizedResponse(exchange, "签名不能为空!");
        }
        if (parameter_signature && StringUtils.isEmpty(nonce_str)) {
            return unauthorizedResponse(exchange, "签名不能为空!");
        }
        if(parameter_signature){
            return authSign(exchange, chain, sign, nonce_str);
        }
        return chain.filter(exchange.mutate().request(mutate.build()).build());
    }
    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
        log.error("[签名异常处理]请求路径:{}", exchange.getRequest().getPath());
        return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.BAD_REQUEST);
    }
    /**
     * 校验签名
     * @return
     */
    private Mono<Void> authSign(ServerWebExchange exchange, GatewayFilterChain chain, String sign, String nonce_str){
        return DataBufferUtils.join(exchange.getRequest().getBody())
                .flatMap(dataBuffer -> {
                    byte[] bytes = new byte[dataBuffer.readableByteCount()];
                    dataBuffer.read(bytes);
                    String bodyString = new String(bytes, StandardCharsets.UTF_8);
                    log.info("请求参数:{}", bodyString);
                    if(!authSign(JSON.parseObject(bodyString), sign, nonce_str)){
                        return unauthorizedResponse(exchange, "签名验证失败!");
                    }
                    DataBufferUtils.release(dataBuffer);
                    Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                        DataBuffer buffer = exchange.getResponse().bufferFactory()
                                .wrap(bytes);
                        return Mono.just(buffer);
                    });
                    ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                        @Override
                        public Flux<DataBuffer> getBody() {
                            return cachedFlux;
                        }
                    };
                    return chain.filter(exchange.mutate().request(mutatedRequest)
                            .build());
        });
    }
    /**
     * 签名校验
     * @return
     */
    private boolean authSign(JSONObject jsonStr, String sign, String nonce_str) {
        System.err.println("请求签名:" + sign);
        String signUrlEncode = localSignUrl(jsonStr, nonce_str);
        signUrlEncode = signUrlEncode.replaceAll("& #40;", "\\(")
                .replaceAll("& #41;", "\\)");
        if(sign.equals(signUrlEncode)){
            return true;
        }
        System.err.println("签名值:" + signUrlEncode);
        return false;
    }
    /**
     * 组装签名路径
     * @param params
     * @return
     */
    public static String localSignUrl(JSONObject params, String key) {
        List<String> keySet = new ArrayList<>(params.keySet());
        // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
        Collections.sort(keySet, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        // 构造签名键值对的格式
        StringBuilder sb = new StringBuilder();
        for (String k : keySet) {
            String v = params.getString(k);
            if(StringUtils.isNotEmpty(v)){
                sb.append(k + "=" + v + "&");
            }
        }
        String signUrl = sb.substring(0, sb.length() - 1);
        System.err.println("签名串:" + signUrl);
        return signUrlEncode(signUrl, key);
    }
    /**
     * 签名字符串加密
     * @param signUrl
     * @param encryptKey
     * @return
     */
    public static String signUrlEncode(String signUrl, String encryptKey) {
        byte[] signByte = new byte[0];
        try {
            signByte = HMACSHA1.HmacSHA1Encrypt(signUrl, encryptKey);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
        String localSign = Base64.encodeBase64String(signByte);
        return localSign;
    }
    @Override
    public int getOrder()
    {
        return -200;
    }
   private static final Logger log = LoggerFactory.getLogger(SignFilter.class);
   // 排除过滤的 uri 地址,nacos自行添加
   @Autowired
   private IgnoreWhiteProperties ignoreWhite;
   @Autowired
   private RedisService redisService;
   @Value("${security.sign}")
   private boolean parameter_signature;
   /**
    * 组装签名路径
    *
    * @param params
    * @return
    */
   public static String localSignUrl(JSONObject params, String key) {
      List<String> keySet = new ArrayList<>(params.keySet());
      // 对所有传入参数按照字段名的 ASCII 码从小到大排序(字典序)
      Collections.sort(keySet, new Comparator<String>() {
         @Override
         public int compare(String o1, String o2) {
            return o1.compareTo(o2);
         }
      });
      // 构造签名键值对的格式
      StringBuilder sb = new StringBuilder();
      for (String k : keySet) {
         String v = params.getString(k);
         if (StringUtils.isNotEmpty(v)) {
            sb.append(k + "=" + v + "&");
         }
      }
      String signUrl = sb.substring(0, sb.length() - 1);
      System.err.println("签名串:" + signUrl);
      return signUrlEncode(signUrl, key);
   }
   /**
    * 签名字符串加密
    *
    * @param signUrl
    * @param encryptKey
    * @return
    */
   public static String signUrlEncode(String signUrl, String encryptKey) {
      byte[] signByte = new byte[0];
      try {
         signByte = HMACSHA1.HmacSHA1Encrypt(signUrl, encryptKey);
      } catch (Exception e) {
         e.printStackTrace();
         throw new RuntimeException(e);
      }
      String localSign = Base64.encodeBase64String(signByte);
      return localSign;
   }
   @Override
   public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
      ServerHttpRequest request = exchange.getRequest();
      ServerHttpRequest.Builder mutate = request.mutate();
      HttpMethod method = request.getMethod();
      if (method != HttpMethod.POST) {
         return chain.filter(exchange.mutate().request(mutate.build()).build());
      }
      String sign = request.getHeaders().getFirst(TokenConstants.SIGN);
      String nonce_str = request.getHeaders().getFirst(TokenConstants.NONCE_STR);
      if (parameter_signature && StringUtils.isEmpty(sign)) {
         return unauthorizedResponse(exchange, "签名不能为空!");
      }
      if (parameter_signature && StringUtils.isEmpty(nonce_str)) {
         return unauthorizedResponse(exchange, "签名不能为空!");
      }
      if (parameter_signature) {
         return authSign(exchange, chain, sign, nonce_str);
      }
      return chain.filter(exchange.mutate().request(mutate.build()).build());
   }
   private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
      log.error("[签名异常处理]请求路径:{}", exchange.getRequest().getPath());
      return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.BAD_REQUEST);
   }
   /**
    * 校验签名
    *
    * @return
    */
   private Mono<Void> authSign(ServerWebExchange exchange, GatewayFilterChain chain, String sign, String nonce_str) {
      return DataBufferUtils.join(exchange.getRequest().getBody())
            .flatMap(dataBuffer -> {
               byte[] bytes = new byte[dataBuffer.readableByteCount()];
               dataBuffer.read(bytes);
               String bodyString = new String(bytes, StandardCharsets.UTF_8);
               log.info("请求参数:{}", bodyString);
               if (!authSign(JSON.parseObject(bodyString), sign, nonce_str)) {
                  return unauthorizedResponse(exchange, "签名验证失败!");
               }
               DataBufferUtils.release(dataBuffer);
               Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                  DataBuffer buffer = exchange.getResponse().bufferFactory()
                        .wrap(bytes);
                  return Mono.just(buffer);
               });
               ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                  @Override
                  public Flux<DataBuffer> getBody() {
                     return cachedFlux;
                  }
               };
               return chain.filter(exchange.mutate().request(mutatedRequest)
                     .build());
            });
   }
   /**
    * 签名校验
    *
    * @return
    */
   private boolean authSign(JSONObject jsonStr, String sign, String nonce_str) {
      System.err.println("请求签名:" + sign);
      String signUrlEncode = localSignUrl(jsonStr, nonce_str);
      signUrlEncode = signUrlEncode.replaceAll("& #40;", "\\(")
            .replaceAll("& #41;", "\\)");
      if (sign.equals(signUrlEncode)) {
         return true;
      }
      System.err.println("签名值:" + signUrlEncode);
      return false;
   }
   @Override
   public int getOrder() {
      return -200;
   }
}