网关对请求解密响应加密
springcloud gateway 中添加filter对请求进行加解密
package com.xxx.xxx.gateway.filter;import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.xxx.xxx.gateway.util.ESafeUtils;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;/*** 请求加解密过滤器*/
@Component
public class MessageEncryptDecryptFilter implements GlobalFilter, Ordered, GatewayFilter {private static final Logger logger = LoggerFactory.getLogger(MessageEncryptDecryptFilter.class);private static final Set<HttpMethod> SUPPORTED_METHOD = new HashSet<HttpMethod>() {{add(HttpMethod.POST);}};private static final Pattern URI_PATTERN = Pattern.compile(".*/api/v1/chat/([^/]+)/message$");@Autowiredprivate ESafeUtils esafeUtils;@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 获取请求体ServerHttpRequest request = exchange.getRequest();// 获取响应体ServerHttpResponse response = exchange.getResponse();// 请求方法HttpMethod method = request.getMethod();// 请求路径String path = exchange.getRequest().getPath().value();logger.info("ApiFilter start path:{}", path);// 路径匹配if (!SUPPORTED_METHOD.contains(method) || !URI_PATTERN.matcher(path).matches()) {return chain.filter(exchange);}logger.info("ApiFilter filter path:{}", path);return DataBufferUtils.join(request.getBody()).flatMap(dataBuffer -> {try {// 获取请求参数String originalRequestBody = getOriginalRequestBody(dataBuffer);logger.info("ApiFilter filter request:{}", originalRequestBody);// 转换JSONObject obj;if (originalRequestBody.startsWith("{")) {obj = JSONUtil.parseObj(originalRequestBody);} else {obj = parseQueryString(originalRequestBody);}logger.info("ApiFilter filter obj:{}", JSONUtil.toJsonStr(obj));// 校验是否需要解密boolean encrypt = checkEncrypt(obj);if (!encrypt) {return chain.filter(exchange);}// 解密请求参数Map<String, String> result = esafeUtils.decrypt(obj.getStr("dataFun"));String plain = result.get("plain");String sm4Key = result.get("sm4Key");// base64测试
// String plain = decode(obj.getStr("dataFun"));
// String sm4Key = "1234567890";// 装饰新的请求体ServerHttpRequestDecorator requestDecorator = serverHttpRequestDecorator(request, plain);// 装饰新的响应体ServerHttpResponseDecorator responseDecorator = serverHttpResponseDecorator(exchange, response, sm4Key);// 使用新的请求和响应转发ServerWebExchange serverWebExchange = exchange.mutate().request(requestDecorator).response(responseDecorator).build();logger.info("ApiFilter end path:{}", path);return chain.filter(serverWebExchange);} catch (Exception e) {logger.error("请求解密异常", e);return Mono.empty();} finally {DataBufferUtils.release(dataBuffer);}});}@Overridepublic int getOrder() {return -1; // 必须<0}/*** 获取原始请求参数*/private String getOriginalRequestBody(DataBuffer dataBuffer) {byte[] bytes = new byte[dataBuffer.readableByteCount()];dataBuffer.read(bytes);return new String(bytes, StandardCharsets.UTF_8);}private ServerHttpRequestDecorator serverHttpRequestDecorator(ServerHttpRequest request, String plain) {return new ServerHttpRequestDecorator(request) {@Overridepublic Flux<DataBuffer> getBody() {byte[] bytes = plain.getBytes(StandardCharsets.UTF_8);return Flux.just(new DefaultDataBufferFactory().wrap(bytes));}@Overridepublic HttpHeaders getHeaders() {HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.putAll(super.getHeaders());httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);httpHeaders.setContentType(MediaType.APPLICATION_JSON);httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");return httpHeaders;}};}private ServerHttpResponseDecorator serverHttpResponseDecorator(ServerWebExchange exchange, ServerHttpResponse originalResponse, String sm4Key) {DataBufferFactory dataBufferFactory = originalResponse.bufferFactory();return new ServerHttpResponseDecorator(originalResponse) {@Overridepublic Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {return super.writeWith(Flux.from(body).collectList().flatMap(dataBuffers -> {// 合并数据缓冲区DataBuffer join = dataBufferFactory.join(dataBuffers);byte[] content = new byte[join.readableByteCount()];join.read(content);DataBufferUtils.release(join);String originalResponseBody = new String(content, StandardCharsets.UTF_8);logger.info("ApiFilter filter response:{}", originalResponseBody);try {// base64测试
// String encryptedBody = Base64.getEncoder().encodeToString(responseBody.getBytes());String encryptedBody = esafeUtils.encrypt(originalResponseBody, sm4Key);JSONObject result = new JSONObject();result.set("dataFun", encryptedBody);result.set("encryptFun", true);byte[] encryptedBytes = JSONUtil.toJsonStr(result).getBytes(StandardCharsets.UTF_8);// 更新响应头HttpHeaders headers = originalResponse.getHeaders();headers.setContentLength(encryptedBytes.length);headers.setContentType(MediaType.APPLICATION_JSON);return Mono.just(dataBufferFactory.wrap(encryptedBytes));} catch (Exception e) {logger.error("响应加密异常", e);exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);return Mono.empty();}}));}@Overridepublic Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {return writeWith(Flux.from(body).flatMapSequential(p -> p));}@Overridepublic HttpHeaders getHeaders() {HttpHeaders headers = new HttpHeaders();headers.putAll(originalResponse.getHeaders());return headers;}};}private boolean checkEncrypt(JSONObject obj) {return obj.getBool("encryptFun", false);}public static void main(String[] args) {String plain = "{\"name\": \"admin\",\"password\": \"123456\"}";String encode = Base64.getEncoder().encodeToString(plain.getBytes());System.out.println(encode);byte[] decode = Base64.getDecoder().decode("eyJlcnJvciI6ICJBR1c6c2lnbmF0dXJl6aqM562+5aSx6LSlIn0=");System.out.println(new String(decode));}private String decode(String encode) {return new String(Base64.getDecoder().decode(encode));}private JSONObject parseQueryString(String queryString) {JSONObject jsonObject = new JSONObject();String[] pairs = queryString.split("&");for (String pair : pairs) {int idx = pair.indexOf("=");if (idx > 0) {// 解码key和value,以防它们是URL编码的String key = URLUtil.decode(pair.substring(0, idx), StandardCharsets.UTF_8);String value = URLUtil.decode(pair.substring(idx + 1), StandardCharsets.UTF_8);// 将键值对添加到JSONObject中jsonObject.set(key, value);}}return jsonObject;}
}
在网关添加一个本地controller的转发规则,然后在gateway项目写一个controller接口进行测试
- id: test routeuri: http://localhost:${server.port} #指向自身(Gateway) predicates:- Path=/test/** #通过此路径访问filters:- RewritePath=/test/(?<segment>.*), /$\{segment} #重写路径到Controller
localhost:8000/test/api/v1/chat/888888/message?token=123456