如何通过Spring Cloud Gateway API实现全信道加解密功能?

2026-04-29 20:252阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1870个文字,预计阅读时间需要8分钟。

如何通过Spring Cloud Gateway API实现全信道加解密功能?

1. 接口使用范围:get 请求、post 请求、put 请求、delete 请求

2.注意事项:请求头增加一个加密版本字段,标识当前的加密算法版本:crypto-version: 1.0.0

如何通过Spring Cloud Gateway API实现全信道加解密功能?

3.加密算法:考虑全局加密,使用加密算法

1. 接口的使用范围

get 请求 post请求 put请求 delete请求 白名单除外

2注意事项

请求头增加一个加密版本字段,标识当前的加密算法版本:crypto-version: 1.0.0

3.加密算法

考虑到全局加密,使用AES加密方式性能更高

加密字符串:原始数据 > AES加密后的字节数组 > Base64编码处理

解密字符串:Base64密文 > AES密文 -> 原始字符串

4.报文格式

GET url:/app/xx/xx?xx=1 加密处理 秘钥:xxxxxxxxxxxxxxxx 加密文本:{"xx":1} 密文:xq4YR89LgUs4V5N5juKgW5hIsiOsCxBOwzX632S8NV4= 加密后的请求 /app/xx/xx?data=xq4YR89LgUs4V5N5juKgW5hIsiOsCxBOwzX632S8NV4= POST url:/app/xx/xx/xxx json body: {"xxx1":"111","xxx2":"huawei","xxx3":"789","xxx4":101,"xxx5":2} 加密处理 秘钥:xxxxxxxxxxxxxxxx 加密文本: {"xxx1":"111","xxx2":"huawei","xxx3":"789","xxx4":101,"xxx5":2} 密文:1oUTYvWfyaeTJ5/wJTVBqUv0Dz0IAUQTZtxSKY9WLZZl8pILP2Sozk5yOYg9I1WTvzgbbGRDGcWV1ASpYykyS1Fq5cT8s3aLXQ6NMo0AaMOC9L0aVpR863qWso5O8aG3 加密后的请求* json body: { "data": "1oUTYvWfyaeTJ5/wJTVBqUv0Dz0IAUQTZtxSKY9WLZZl8pILP2Sozk5yOYg9I1WTvzgbbGRDGcWV1ASpYykyS1Fq5cT8s3aLXQ6NMo0AaMOjt4G9dK0WwhMGZofYuBKmdF27R8Qkr3VtZvjadtvBazJurITyE7hFcr43nlHSL5E=" } POST url传参 和GET格式一致

5.直接上代码

import cn.hutool.crypto.CryptoException; import cn.iocoder.yudao.gateway.util.AesUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.google.common.collect.Lists; import io.netty.buffer.ByteBufAllocator; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringEscapeUtils; import org.reactivestreams.Publisher; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter; import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter; import org.springframework.context.annotation.Configuration; 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.NettyDataBufferFactory; 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.util.AntPathMatcher; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.*; @Slf4j @RequiredArgsConstructor @Configuration @ConditionalOnProperty(value = "gateway.crypto.enabled", havingValue = "true", matchIfMissing = true) public class CryptoFilter implements GlobalFilter, Ordered { private final AntPathMatcher antPathMatcher = new AntPathMatcher(); private final DataBufferFactory dataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT); private final CryptoProperties cryptoProperties; @Override //1001 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("来了老弟"); if (!cryptoProperties.isEnabled()) { return chain.filter(exchange); } ServerHttpRequest request = exchange.getRequest(); //校验请求路径跳过加密 // String originalRequestUrl = RequestProvider.getOriginalRequestUrl(exchange); String path = exchange.getRequest().getURI().getPath(); if (isSkip(path)) { return chain.filter(exchange); } HttpHeaders headers = request.getHeaders(); MediaType contentType = headers.getContentType(); //后期算法升级扩展,暂时只判断是否相等 if (!cryptoProperties.getCryptoVersion().equals(headers.getFirst(cryptoProperties.getCryptoVersionHeader()))) { return Mono.error(new CryptoException("加密版本不支持")); } if (request.getMethod() == HttpMethod.GET || request.getMethod() == HttpMethod.DELETE ) { return this.handleGetReq(exchange, chain); } else if (request.getMethod() == HttpMethod.POST && (contentType == null || MediaType.APPLICATION_JSON.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType))) { return this.handlePostReq(exchange, chain); } else if (request.getMethod() == HttpMethod.PUT && (contentType == null || MediaType.APPLICATION_JSON.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType))) { return this.handlePostReq(exchange, chain); } else { return chain.filter(exchange); } } class CryptoServerHttpResponseDecorator extends ServerHttpResponseDecorator { final DataBufferFactory bufferFactory; boolean isPass = false; public CryptoServerHttpResponseDecorator(ServerHttpResponse delegate) { super(delegate); bufferFactory = delegate.bufferFactory(); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = super.getHeaders(); log.info("同一个请求此处有可能调用多次,先重置为false"); //同一个请求此处有可能调用多次,先重置为false isPass = false; if (headers.getContentType() != null && !MediaType.APPLICATION_JSON.equals(headers.getContentType()) && !MediaType.APPLICATION_JSON_UTF8.equals(headers.getContentType())) { //相应体ContentType只处理json isPass = true; } else if (!headers.containsKey(cryptoProperties.getCryptoVersionHeader())) { //添加version响应头 headers.add(cryptoProperties.getCryptoVersionHeader(), cryptoProperties.getCryptoVersion()); } return headers; } //调用 writeWith 和 writeAndFlushWith 判断: NettyWriteResponseFilter // application/json;charset=UTF-8 走这里 @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { if (body instanceof Flux && !isPass) { Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body; return super.writeWith(fluxBody.buffer().map(dataBuffer -> { DataBuffer joinDataBuffer = bufferFactory.join(dataBuffer); byte[] content = new byte[joinDataBuffer.readableByteCount()]; joinDataBuffer.read(content); DataBufferUtils.release(joinDataBuffer); Map<String, String> data = new HashMap<>(1); data.put(cryptoProperties.getParamName(), AesUtil.encryptToBase64(content, cryptoProperties.getAesKey())); return bufferFactory.wrap(JSON.toJSONString(data).getBytes(StandardCharsets.UTF_8)); })); } return super.writeWith(body); } // StreamingMediaType类型:application/stream 和 application/stream+json 走这里 @Override public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) { return super.writeAndFlushWith(body); } } @SneakyThrows private Mono<Void> handlePostReq(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String paramData = request.getQueryParams().getFirst(cryptoProperties.getParamName()); MultiValueMap<String, String> queryParamMap = new LinkedMultiValueMap<>(); final boolean queryParamsDecrypt = !StringUtils.isEmpty(paramData); if (queryParamsDecrypt) { String dataJson; try { //AES解密 dataJson = AesUtil.decryptFormBase64ToString(paramData, cryptoProperties.getAesKey()); } catch (Exception e) { log.error("请求参数解密异常: ", e); return cryptoError(exchange.getResponse(), "请求参数解密异常"); } //构造查询参数Map queryParamMap = buildMultiValueMap(dataJson); //新的解密后的uri request request = this.buildNewServerHttpRequest(request, queryParamMap); } //构造一个请求包装 final MultiValueMap<String, String> finalQueryParamMap = new LinkedMultiValueMap<>(queryParamMap); ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(request) { @Override public HttpHeaders getHeaders() { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.putAll(super.getHeaders()); httpHeaders.remove(HttpHeaders.CONTENT_LENGTH); httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); return httpHeaders; } @Override public MultiValueMap<String, String> getQueryParams() { if (queryParamsDecrypt) { return finalQueryParamMap; } return super.getQueryParams(); } @Override public Flux<DataBuffer> getBody() { //注意: 这里需要buffer,拿到完整报文后再map解密 return super.getBody().buffer().map(buffer -> { DataBuffer joinDataBuffer = dataBufferFactory.join(buffer); byte[] content = new byte[joinDataBuffer.readableByteCount()]; joinDataBuffer.read(content); DataBufferUtils.release(joinDataBuffer); String decryptData = new String(content, StandardCharsets.UTF_8); log.info("post decryptData: {}", decryptData); if (!queryParamsDecrypt && StringUtils.isEmpty(decryptData)) { throw new CryptoException("参数格式错误"); } else { JSONObject dataJsonObj = JSON.parseObject(decryptData); if (!queryParamsDecrypt && !dataJsonObj.containsKey(cryptoProperties.getParamName())) { throw new CryptoException("参数格式错误"); } byte[] bytes = AesUtil.decryptFormBase64(dataJsonObj.getString(cryptoProperties.getParamName()), cryptoProperties.getAesKey()); return dataBufferFactory.wrap(Objects.requireNonNull(bytes)); } }); } }; return chain.filter(exchange.mutate() .request(decorator) .response(new CryptoServerHttpResponseDecorator(exchange.getResponse())) .build()); } @SneakyThrows private Mono<Void> handleGetReq(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); if (request.getQueryParams().isEmpty()) { // get无参数 不走参数解密 return chain.filter(exchange.mutate() .request(request) .response(new CryptoServerHttpResponseDecorator(exchange.getResponse())) .build()); } String paramData = request.getQueryParams().getFirst(cryptoProperties.getParamName()); if (StringUtils.isEmpty(paramData)) { //有参数但是密文字段不存在 throw new CryptoException("参数格式错误"); } String dataJson; String s; try { //AES解密 dataJson = AesUtil.decryptFormBase64ToString(paramData, cryptoProperties.getAesKey()); JSONObject jsonObject = JSON.parseObject(dataJson); System.out.println("jsonObject = " + jsonObject); Iterator<Map.Entry<String, Object>> iterator = jsonObject.entrySet().iterator(); Map.Entry<String, Object> entry; while (iterator.hasNext()) { entry = iterator.next(); if (entry.getValue()==null){ iterator.remove(); continue; } if (entry.getValue().equals("")){ iterator.remove(); } System.out.println("key值是:"+entry.getKey()); System.out.println("value是:"+entry.getValue()); } System.out.println("jsonObject = " + jsonObject); s = JSON.toJSONString(jsonObject); System.out.println("s = " + s); } catch (Exception e) { log.error("请求参数解密异常: ", e); return cryptoError(exchange.getResponse(), "请求参数解密异常"); } //构造查询参数Map MultiValueMap<String, String> map = buildMultiValueMap(s); //新的解密后的uri ServerHttpRequest newHttpRequest = this.buildNewServerHttpRequest(request, map); //新的解密后的uri request ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(newHttpRequest) { @Override public MultiValueMap<String, String> getQueryParams() { return map; } }; return chain.filter(exchange.mutate() .request(decorator) .response(new CryptoServerHttpResponseDecorator(exchange.getResponse())) .build()); } private MultiValueMap<String, String> buildMultiValueMap(String dataJson) { JSONObject jsonObject = JSON.parseObject(dataJson); MultiValueMap<String, String> map = new LinkedMultiValueMap<>(jsonObject.size()); for (String key : jsonObject.keySet()) { Object o = jsonObject.get(key); if(o instanceof List){ List li= (List) o; map.put(key, li); }else { map.put(key, Lists.newArrayList(jsonObject.getString(key))); } } return map; } private ServerHttpRequest buildNewServerHttpRequest(ServerHttpRequest request, MultiValueMap<String, String> params) throws Exception { StringBuilder queryBuilder = new StringBuilder(); for (String key : params.keySet()) { List<String> strings = params.get(key); if(strings.size()==1){ queryBuilder.append(key); queryBuilder.append(StringPool.EQUALS); queryBuilder.append(params.getFirst(key)); queryBuilder.append(StringPool.AMPERSAND); }else { for (int i = 0; i < strings.size(); i++) { queryBuilder.append(key); queryBuilder.append(StringPool.EQUALS); queryBuilder.append(strings.get(i)); queryBuilder.append(StringPool.AMPERSAND); } } } queryBuilder.deleteCharAt(queryBuilder.length() - 1); //经过测试只覆盖 ServerHttpRequest的getQueryParams路由分发之后,无法携带过去新的参数,所以这里需要构造一个新的解密后的uri URI uri = request.getURI(); URI newUri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), queryBuilder.toString(), uri.getFragment()); //构造一个新的ServerHttpRequest return request.mutate().uri(newUri).build(); } private boolean isSkip(String path) { for (int i = 0; i < cryptoProperties.getSkipPathPattern().size(); i++) { String skipPathPattern = cryptoProperties.getSkipPathPattern().get(i); if (antPathMatcher.match(skipPathPattern,path)){ return true; } } return false; } private Mono<Void> cryptoError(ServerHttpResponse resp, String msg) { resp.setStatusCode(HttpStatus.UNAUTHORIZED); resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); String result = JSON.toJSONString(ResponseProvider.unAuth(msg)); DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8)); return resp.writeWith(Flux.just(buffer)); } @Override public int getOrder() { return -200; } public static void main(String[] args) { System.out.println(UUID.randomUUID().toString()); } }

还缺少相应的工具类 下期继续

本文共计1870个文字,预计阅读时间需要8分钟。

如何通过Spring Cloud Gateway API实现全信道加解密功能?

1. 接口使用范围:get 请求、post 请求、put 请求、delete 请求

2.注意事项:请求头增加一个加密版本字段,标识当前的加密算法版本:crypto-version: 1.0.0

如何通过Spring Cloud Gateway API实现全信道加解密功能?

3.加密算法:考虑全局加密,使用加密算法

1. 接口的使用范围

get 请求 post请求 put请求 delete请求 白名单除外

2注意事项

请求头增加一个加密版本字段,标识当前的加密算法版本:crypto-version: 1.0.0

3.加密算法

考虑到全局加密,使用AES加密方式性能更高

加密字符串:原始数据 > AES加密后的字节数组 > Base64编码处理

解密字符串:Base64密文 > AES密文 -> 原始字符串

4.报文格式

GET url:/app/xx/xx?xx=1 加密处理 秘钥:xxxxxxxxxxxxxxxx 加密文本:{"xx":1} 密文:xq4YR89LgUs4V5N5juKgW5hIsiOsCxBOwzX632S8NV4= 加密后的请求 /app/xx/xx?data=xq4YR89LgUs4V5N5juKgW5hIsiOsCxBOwzX632S8NV4= POST url:/app/xx/xx/xxx json body: {"xxx1":"111","xxx2":"huawei","xxx3":"789","xxx4":101,"xxx5":2} 加密处理 秘钥:xxxxxxxxxxxxxxxx 加密文本: {"xxx1":"111","xxx2":"huawei","xxx3":"789","xxx4":101,"xxx5":2} 密文:1oUTYvWfyaeTJ5/wJTVBqUv0Dz0IAUQTZtxSKY9WLZZl8pILP2Sozk5yOYg9I1WTvzgbbGRDGcWV1ASpYykyS1Fq5cT8s3aLXQ6NMo0AaMOC9L0aVpR863qWso5O8aG3 加密后的请求* json body: { "data": "1oUTYvWfyaeTJ5/wJTVBqUv0Dz0IAUQTZtxSKY9WLZZl8pILP2Sozk5yOYg9I1WTvzgbbGRDGcWV1ASpYykyS1Fq5cT8s3aLXQ6NMo0AaMOjt4G9dK0WwhMGZofYuBKmdF27R8Qkr3VtZvjadtvBazJurITyE7hFcr43nlHSL5E=" } POST url传参 和GET格式一致

5.直接上代码

import cn.hutool.crypto.CryptoException; import cn.iocoder.yudao.gateway.util.AesUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.google.common.collect.Lists; import io.netty.buffer.ByteBufAllocator; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringEscapeUtils; import org.reactivestreams.Publisher; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter; import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter; import org.springframework.context.annotation.Configuration; 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.NettyDataBufferFactory; 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.util.AntPathMatcher; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.*; @Slf4j @RequiredArgsConstructor @Configuration @ConditionalOnProperty(value = "gateway.crypto.enabled", havingValue = "true", matchIfMissing = true) public class CryptoFilter implements GlobalFilter, Ordered { private final AntPathMatcher antPathMatcher = new AntPathMatcher(); private final DataBufferFactory dataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT); private final CryptoProperties cryptoProperties; @Override //1001 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("来了老弟"); if (!cryptoProperties.isEnabled()) { return chain.filter(exchange); } ServerHttpRequest request = exchange.getRequest(); //校验请求路径跳过加密 // String originalRequestUrl = RequestProvider.getOriginalRequestUrl(exchange); String path = exchange.getRequest().getURI().getPath(); if (isSkip(path)) { return chain.filter(exchange); } HttpHeaders headers = request.getHeaders(); MediaType contentType = headers.getContentType(); //后期算法升级扩展,暂时只判断是否相等 if (!cryptoProperties.getCryptoVersion().equals(headers.getFirst(cryptoProperties.getCryptoVersionHeader()))) { return Mono.error(new CryptoException("加密版本不支持")); } if (request.getMethod() == HttpMethod.GET || request.getMethod() == HttpMethod.DELETE ) { return this.handleGetReq(exchange, chain); } else if (request.getMethod() == HttpMethod.POST && (contentType == null || MediaType.APPLICATION_JSON.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType))) { return this.handlePostReq(exchange, chain); } else if (request.getMethod() == HttpMethod.PUT && (contentType == null || MediaType.APPLICATION_JSON.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType))) { return this.handlePostReq(exchange, chain); } else { return chain.filter(exchange); } } class CryptoServerHttpResponseDecorator extends ServerHttpResponseDecorator { final DataBufferFactory bufferFactory; boolean isPass = false; public CryptoServerHttpResponseDecorator(ServerHttpResponse delegate) { super(delegate); bufferFactory = delegate.bufferFactory(); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = super.getHeaders(); log.info("同一个请求此处有可能调用多次,先重置为false"); //同一个请求此处有可能调用多次,先重置为false isPass = false; if (headers.getContentType() != null && !MediaType.APPLICATION_JSON.equals(headers.getContentType()) && !MediaType.APPLICATION_JSON_UTF8.equals(headers.getContentType())) { //相应体ContentType只处理json isPass = true; } else if (!headers.containsKey(cryptoProperties.getCryptoVersionHeader())) { //添加version响应头 headers.add(cryptoProperties.getCryptoVersionHeader(), cryptoProperties.getCryptoVersion()); } return headers; } //调用 writeWith 和 writeAndFlushWith 判断: NettyWriteResponseFilter // application/json;charset=UTF-8 走这里 @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { if (body instanceof Flux && !isPass) { Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body; return super.writeWith(fluxBody.buffer().map(dataBuffer -> { DataBuffer joinDataBuffer = bufferFactory.join(dataBuffer); byte[] content = new byte[joinDataBuffer.readableByteCount()]; joinDataBuffer.read(content); DataBufferUtils.release(joinDataBuffer); Map<String, String> data = new HashMap<>(1); data.put(cryptoProperties.getParamName(), AesUtil.encryptToBase64(content, cryptoProperties.getAesKey())); return bufferFactory.wrap(JSON.toJSONString(data).getBytes(StandardCharsets.UTF_8)); })); } return super.writeWith(body); } // StreamingMediaType类型:application/stream 和 application/stream+json 走这里 @Override public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) { return super.writeAndFlushWith(body); } } @SneakyThrows private Mono<Void> handlePostReq(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String paramData = request.getQueryParams().getFirst(cryptoProperties.getParamName()); MultiValueMap<String, String> queryParamMap = new LinkedMultiValueMap<>(); final boolean queryParamsDecrypt = !StringUtils.isEmpty(paramData); if (queryParamsDecrypt) { String dataJson; try { //AES解密 dataJson = AesUtil.decryptFormBase64ToString(paramData, cryptoProperties.getAesKey()); } catch (Exception e) { log.error("请求参数解密异常: ", e); return cryptoError(exchange.getResponse(), "请求参数解密异常"); } //构造查询参数Map queryParamMap = buildMultiValueMap(dataJson); //新的解密后的uri request request = this.buildNewServerHttpRequest(request, queryParamMap); } //构造一个请求包装 final MultiValueMap<String, String> finalQueryParamMap = new LinkedMultiValueMap<>(queryParamMap); ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(request) { @Override public HttpHeaders getHeaders() { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.putAll(super.getHeaders()); httpHeaders.remove(HttpHeaders.CONTENT_LENGTH); httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); return httpHeaders; } @Override public MultiValueMap<String, String> getQueryParams() { if (queryParamsDecrypt) { return finalQueryParamMap; } return super.getQueryParams(); } @Override public Flux<DataBuffer> getBody() { //注意: 这里需要buffer,拿到完整报文后再map解密 return super.getBody().buffer().map(buffer -> { DataBuffer joinDataBuffer = dataBufferFactory.join(buffer); byte[] content = new byte[joinDataBuffer.readableByteCount()]; joinDataBuffer.read(content); DataBufferUtils.release(joinDataBuffer); String decryptData = new String(content, StandardCharsets.UTF_8); log.info("post decryptData: {}", decryptData); if (!queryParamsDecrypt && StringUtils.isEmpty(decryptData)) { throw new CryptoException("参数格式错误"); } else { JSONObject dataJsonObj = JSON.parseObject(decryptData); if (!queryParamsDecrypt && !dataJsonObj.containsKey(cryptoProperties.getParamName())) { throw new CryptoException("参数格式错误"); } byte[] bytes = AesUtil.decryptFormBase64(dataJsonObj.getString(cryptoProperties.getParamName()), cryptoProperties.getAesKey()); return dataBufferFactory.wrap(Objects.requireNonNull(bytes)); } }); } }; return chain.filter(exchange.mutate() .request(decorator) .response(new CryptoServerHttpResponseDecorator(exchange.getResponse())) .build()); } @SneakyThrows private Mono<Void> handleGetReq(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); if (request.getQueryParams().isEmpty()) { // get无参数 不走参数解密 return chain.filter(exchange.mutate() .request(request) .response(new CryptoServerHttpResponseDecorator(exchange.getResponse())) .build()); } String paramData = request.getQueryParams().getFirst(cryptoProperties.getParamName()); if (StringUtils.isEmpty(paramData)) { //有参数但是密文字段不存在 throw new CryptoException("参数格式错误"); } String dataJson; String s; try { //AES解密 dataJson = AesUtil.decryptFormBase64ToString(paramData, cryptoProperties.getAesKey()); JSONObject jsonObject = JSON.parseObject(dataJson); System.out.println("jsonObject = " + jsonObject); Iterator<Map.Entry<String, Object>> iterator = jsonObject.entrySet().iterator(); Map.Entry<String, Object> entry; while (iterator.hasNext()) { entry = iterator.next(); if (entry.getValue()==null){ iterator.remove(); continue; } if (entry.getValue().equals("")){ iterator.remove(); } System.out.println("key值是:"+entry.getKey()); System.out.println("value是:"+entry.getValue()); } System.out.println("jsonObject = " + jsonObject); s = JSON.toJSONString(jsonObject); System.out.println("s = " + s); } catch (Exception e) { log.error("请求参数解密异常: ", e); return cryptoError(exchange.getResponse(), "请求参数解密异常"); } //构造查询参数Map MultiValueMap<String, String> map = buildMultiValueMap(s); //新的解密后的uri ServerHttpRequest newHttpRequest = this.buildNewServerHttpRequest(request, map); //新的解密后的uri request ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(newHttpRequest) { @Override public MultiValueMap<String, String> getQueryParams() { return map; } }; return chain.filter(exchange.mutate() .request(decorator) .response(new CryptoServerHttpResponseDecorator(exchange.getResponse())) .build()); } private MultiValueMap<String, String> buildMultiValueMap(String dataJson) { JSONObject jsonObject = JSON.parseObject(dataJson); MultiValueMap<String, String> map = new LinkedMultiValueMap<>(jsonObject.size()); for (String key : jsonObject.keySet()) { Object o = jsonObject.get(key); if(o instanceof List){ List li= (List) o; map.put(key, li); }else { map.put(key, Lists.newArrayList(jsonObject.getString(key))); } } return map; } private ServerHttpRequest buildNewServerHttpRequest(ServerHttpRequest request, MultiValueMap<String, String> params) throws Exception { StringBuilder queryBuilder = new StringBuilder(); for (String key : params.keySet()) { List<String> strings = params.get(key); if(strings.size()==1){ queryBuilder.append(key); queryBuilder.append(StringPool.EQUALS); queryBuilder.append(params.getFirst(key)); queryBuilder.append(StringPool.AMPERSAND); }else { for (int i = 0; i < strings.size(); i++) { queryBuilder.append(key); queryBuilder.append(StringPool.EQUALS); queryBuilder.append(strings.get(i)); queryBuilder.append(StringPool.AMPERSAND); } } } queryBuilder.deleteCharAt(queryBuilder.length() - 1); //经过测试只覆盖 ServerHttpRequest的getQueryParams路由分发之后,无法携带过去新的参数,所以这里需要构造一个新的解密后的uri URI uri = request.getURI(); URI newUri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), queryBuilder.toString(), uri.getFragment()); //构造一个新的ServerHttpRequest return request.mutate().uri(newUri).build(); } private boolean isSkip(String path) { for (int i = 0; i < cryptoProperties.getSkipPathPattern().size(); i++) { String skipPathPattern = cryptoProperties.getSkipPathPattern().get(i); if (antPathMatcher.match(skipPathPattern,path)){ return true; } } return false; } private Mono<Void> cryptoError(ServerHttpResponse resp, String msg) { resp.setStatusCode(HttpStatus.UNAUTHORIZED); resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); String result = JSON.toJSONString(ResponseProvider.unAuth(msg)); DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8)); return resp.writeWith(Flux.just(buffer)); } @Override public int getOrder() { return -200; } public static void main(String[] args) { System.out.println(UUID.randomUUID().toString()); } }

还缺少相应的工具类 下期继续