| | |
| | | |
| | | /** |
| | | * 网关鉴权 |
| | | * |
| | | * |
| | | * @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; |
| | | } |
| | | } |