diff --git a/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/encryption/AesCbcUtils.java b/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/encryption/AesCbcUtils.java index c51d403..4862464 100644 --- a/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/encryption/AesCbcUtils.java +++ b/bonus-common/bonus-common-core/src/main/java/com/bonus/common/core/utils/encryption/AesCbcUtils.java @@ -100,7 +100,7 @@ public class AesCbcUtils { */ public static String decrypt(String data) { try{ - String encryptStr=""; + String encryptStr=""; if(StringUtils.isNotEmpty(data)){ encryptStr=data.replace(" ","+"); } diff --git a/bonus-gateway/src/main/java/com/bonus/gateway/filter/AecDecryptParamFilter.java b/bonus-gateway/src/main/java/com/bonus/gateway/filter/AecDecryptParamFilter.java index 8e5e3a6..84aecfc 100644 --- a/bonus-gateway/src/main/java/com/bonus/gateway/filter/AecDecryptParamFilter.java +++ b/bonus-gateway/src/main/java/com/bonus/gateway/filter/AecDecryptParamFilter.java @@ -37,8 +37,7 @@ import java.util.stream.Collectors; @Component public class AecDecryptParamFilter extends AbstractGatewayFilterFactory { - @Value("${system.decryptEnabled}") - public boolean decryptEnabled; + public boolean decryptEnabled = true; public static final String HEADER_NAME = "decrypt"; public static final String HMAC_HEADER_NAME = "Params-Hash"; @@ -140,8 +139,7 @@ public class AecDecryptParamFilter extends AbstractGatewayFilterFactory { throw new CaptchaException("请求参数不正确"); } if (StringUtils.isEmpty(decryptedParam)) { - log.error("解密后的参数为空"); - throw new CaptchaException("请求参数不正确"); + return null; } Map> queryParams = Arrays.stream(decryptedParam.split("&")) .map(param -> param.split("=")) diff --git a/bonus-gateway/src/main/java/com/bonus/gateway/filter/AuthFilter.java b/bonus-gateway/src/main/java/com/bonus/gateway/filter/AuthFilter.java index 17bf709..c94a896 100644 --- a/bonus-gateway/src/main/java/com/bonus/gateway/filter/AuthFilter.java +++ b/bonus-gateway/src/main/java/com/bonus/gateway/filter/AuthFilter.java @@ -32,8 +32,6 @@ import java.util.concurrent.TimeUnit; @Component public class AuthFilter implements GlobalFilter, Ordered { - @Value("${system.decryptEnabled}") - public boolean jaData; private static final Logger log = LoggerFactory.getLogger(AuthFilter.class); /** @@ -117,7 +115,7 @@ public class AuthFilter implements GlobalFilter, Ordered { private Mono unauthorizedResponse(ServerWebExchange exchange, String msg) { log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath()); - return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED, jaData); + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED, true); } /** diff --git a/bonus-gateway/src/main/java/com/bonus/gateway/filter/BlackListUrlFilter.java b/bonus-gateway/src/main/java/com/bonus/gateway/filter/BlackListUrlFilter.java index dc4c82d..f0bf18c 100644 --- a/bonus-gateway/src/main/java/com/bonus/gateway/filter/BlackListUrlFilter.java +++ b/bonus-gateway/src/main/java/com/bonus/gateway/filter/BlackListUrlFilter.java @@ -17,8 +17,7 @@ import com.bonus.common.core.utils.ServletUtils; */ @Component public class BlackListUrlFilter extends AbstractGatewayFilterFactory { - @Value("${system.encryptEnabled}") - public boolean encryptEnabled; + @Override public GatewayFilter apply(Config config) { @@ -26,7 +25,7 @@ public class BlackListUrlFilter extends AbstractGatewayFilterFactory> MESSAGE_READERS = HandlerStrategies.withDefaults().messageReaders(); + @Value("${system.encryptRequest}") + private boolean encryptRequest; + /** + * 是否启用数据完整性校验功能 + */ + @Value("${system.integrity}") + private boolean integrity; + /** + * 数据加密标志 + */ + public static final String ENCRYPT = "data_encrypt_request"; + /** + * 数据完整性校验标志 + */ + public static final String INTEGRALITY = "data_integrity"; + /** + * 完整性校验哈希值 + */ + public static final String HMAC_HEADER_NAME = "Params-Hash"; /** - * ReadFormData + * 处理Web请求,并(可选地)通过给定的 {@link GatewayFilterChain} 委托给下一个 {@code GatewayFilter}。 * - * @param exchange - * @param chain - * @return + * @param exchange 当前服务器交换 + * @param chain 提供委托给下一个过滤器的方式 + * @return {@code Mono} 表示请求处理完成的指示 */ - private Mono readFormData(ServerWebExchange exchange, GatewayFilterChain chain, - GatewayContext gatewayContext) { - final ServerHttpRequest request = exchange.getRequest(); - HttpHeaders headers = request.getHeaders(); - - return exchange.getFormData().doOnNext(multiValueMap -> { - gatewayContext.setFormData(multiValueMap); - log.debug("[GatewayContext]Read FormData:{}", multiValueMap); - }).then(Mono.defer(() -> { - Charset charset = headers.getContentType().getCharset(); - charset = charset == null ? StandardCharsets.UTF_8 : charset; - String charsetName = charset.name(); - MultiValueMap formData = gatewayContext.getFormData(); - MultiValueMap formData2 = new LinkedMultiValueMap<>(); - /** - * formData is empty just return - */ - if (null == formData || formData.isEmpty()) { - return chain.filter(exchange); - } - //是否进行加密 - if (decryptEnabled) { - Object obj = formData.get(SystemGlobal.FORM_DATA); - if (!ObjectUtils.isEmpty(obj)) { - String data = obj.toString(); - data = AesCbcUtils.decrypt(data); - if (StringUtils.isEmpty(data)) { - return CommonConstant.buildResponse(exchange, HttpStatus.BAD_REQUEST.value(), "请输入正确的请求参数"); - } - String[] params = data.split("&"); - for (int i = 0; i < params.length; i++) { - String[] param = params[i].split("="); - formData2.add(param[0], param[1]); - } - formData = formData2; - } else { - //如果是空的 是否去除了加密 - ServerHttpRequest serverHttpRequest = exchange.getRequest(); - String head = serverHttpRequest.getHeaders().getFirst(SystemGlobal.KEY_DECRYPT); - if (StringUtils.isNotEmpty(head) && !SystemGlobal.KEY_DECRYPT.equals(head)) { - return CommonConstant.buildResponse(exchange, HttpStatus.BAD_REQUEST.value(), "请输入正确的请求参数"); - } - - } - } - StringBuilder formDataBodyBuilder = new StringBuilder(); - String entryKey; - List entryValue; - try { - /** - * repackage form data - */ - for (Map.Entry> entry : formData.entrySet()) { - entryKey = entry.getKey(); - entryValue = entry.getValue(); - if (entryValue.size() > 1) { - for (String value : entryValue) { - formDataBodyBuilder.append(entryKey).append("=") - .append(URLEncoder.encode(value, charsetName)).append("&"); - } - } else { - formDataBodyBuilder.append(entryKey).append("=") - .append(URLEncoder.encode(entryValue.get(0), charsetName)).append("&"); - } - } - } catch (UnsupportedEncodingException e) { - // ignore URLEncode Exception - } - /** - * substring with the last char '&' - */ - String formDataBodyString = ""; - if (formDataBodyBuilder.length() > 0) { - formDataBodyString = formDataBodyBuilder.substring(0, formDataBodyBuilder.length() - 1); - } - /** - * get data bytes - */ - byte[] bodyBytes = formDataBodyString.getBytes(charset); - int contentLength = bodyBytes.length; - ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(request) { - /** - * change content-length - * - * @return - */ - @Override - public HttpHeaders getHeaders() { - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.putAll(super.getHeaders()); - if (contentLength > 0) { - httpHeaders.setContentLength(contentLength); - } else { - httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); - } - return httpHeaders; - } - - /** - * read bytes to Flux - * - * @return - */ - @Override - public Flux getBody() { - return DataBufferUtils.read(new ByteArrayResource(bodyBytes), - new NettyDataBufferFactory(ByteBufAllocator.DEFAULT), contentLength); - } - }; - ServerWebExchange mutateExchange = exchange.mutate().request(decorator).build(); - log.info("[GatewayContext]Rewrite Form Data :{}", formDataBodyString); - - return chain.filter(mutateExchange); - })); - } - - /** - * ReadJsonBody - * - * @param exchange 操作的http请求数据 - * @param chain 网关过滤器链表 - * @return - */ - private Mono readBody(ServerWebExchange exchange, GatewayFilterChain chain, GatewayContext gatewayContext) { - /** - * join the body - */ - return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> { - byte[] bytes = new byte[dataBuffer.readableByteCount()]; - dataBuffer.read(bytes); - DataBufferUtils.release(dataBuffer); - Flux cachedFlux = Flux.defer(() -> { - DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes); - DataBufferUtils.retain(buffer); - return Mono.just(buffer); - }); - /** - * repackage ServerHttpRequest - */ - ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) { - @Override - public Flux getBody() { - return cachedFlux; - } - }; - /** - * mutate exchage with new ServerHttpRequest - */ - ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build(); - /** - * read body string with default messageReaders - */ - return ServerRequest.create(mutatedExchange, MESSAGE_READERS).bodyToMono(String.class) - .doOnNext(objectValue -> { - gatewayContext.setCacheBody(objectValue); - log.debug("[GatewayContext]Read JsonBody:{}", objectValue); - }).then(chain.filter(mutatedExchange)); - }); + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + // 如果解密和完整性校验均未启用,则直接通过过滤链 + if (!encryptRequest && !integrity) { + return chain.filter(exchange); + } + ServerHttpRequest request = exchange.getRequest(); + MediaType contentType = request.getHeaders().getContentType(); + if (contentType == null) { + log.info("请求头中无Content-Type信息,直接继续过滤链。"); + return handleUrlParams(exchange, chain); + } else if (contentType.isCompatibleWith(MediaType.APPLICATION_JSON)) { + return handleBodyRequest(exchange, chain); + } else { + return chain.filter(exchange); + } } @Override @@ -230,41 +96,176 @@ public class RequestCoverFilter implements GlobalFilter, Ordered { return HIGHEST_PRECEDENCE; } - @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - /** - * save request path and serviceId into gateway context - */ + /** + * 处理请求体里的参数 + * + * @param exchange 当前服务器交换 + * @param chain 提供委托给下一个过滤器的方式 + * @return {@code Mono} 表示请求处理完成的指示 + */ + private Mono handleBodyRequest(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); + boolean integrality = "true".equalsIgnoreCase(request.getHeaders().getFirst(INTEGRALITY)); + boolean encrypt = "true".equalsIgnoreCase(request.getHeaders().getFirst(ENCRYPT)); - GatewayContext gatewayContext = new GatewayContext(); - String path = request.getPath().pathWithinApplication().value(); - gatewayContext.setPath(path); - gatewayContext.getFormData().addAll(request.getQueryParams()); - gatewayContext.setIpAddress(String.valueOf(request.getRemoteAddress())); - HttpHeaders headers = request.getHeaders(); - gatewayContext.setHeaders(headers); - log.debug("HttpMethod:{},Url:{}", request.getMethod(), request.getURI().getRawPath()); + return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> { + byte[] body = new byte[dataBuffer.readableByteCount()]; + dataBuffer.read(body); + DataBufferUtils.release(dataBuffer); + String requestBody = new String(body, StandardCharsets.UTF_8); - /// 注意,因为webflux的响应式编程 不能再采取原先的编码方式 即应该先将gatewayContext放入exchange中,否则其他地方可能取不到 - /** - * save gateway context into exchange - */ - exchange.getAttributes().put(GatewayContext.CACHE_GATEWAY_CONTEXT, gatewayContext); - - // 处理参数 - MediaType contentType = headers.getContentType(); - long contentLength = headers.getContentLength(); - if (contentLength > 0) { - if (MediaType.APPLICATION_JSON.equals(contentType) || APPLICATION_JSON_UTF8.equals(contentType)) { - return readBody(exchange, chain, gatewayContext); + // 去掉多余的引号(如果有) + if (requestBody.startsWith("\"") && requestBody.endsWith("\"")) { + requestBody = requestBody.substring(1, requestBody.length() - 1); } - if (MediaType.APPLICATION_FORM_URLENCODED.equals(contentType) || contentType.toString().contains(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) { - return readFormData(exchange, chain, gatewayContext); + + if (ObjectUtils.isEmpty(requestBody)) { + return exchange.getResponse().setComplete(); } + // 解密请求体 + if (encryptRequest && encrypt) { + try { + requestBody = AesCbcUtils.decrypt(requestBody); + } catch (Exception e) { + log.error("解密请求体时发生错误: {}", e.getMessage(), e); + exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + return exchange.getResponse().setComplete(); + } + } + + // 校验数据完整性 + if (integrity && integrality) { + String providedHmac = exchange.getRequest().getHeaders().getFirst(HMAC_HEADER_NAME); + integrityVerification(providedHmac, requestBody); + } + + if (ObjectUtils.isEmpty(requestBody)) { + return exchange.getResponse().setComplete(); + } + + // 创建新的请求体 + DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory(); + DataBuffer newBody = bufferFactory.wrap(requestBody.getBytes(StandardCharsets.UTF_8)); + ServerHttpRequest newRequest = createNewRequest(exchange, newBody); + + return chain.filter(exchange.mutate().request(newRequest).build()); + }); + } + + /** + * 创建包含新请求体的请求 + * + * @param exchange 当前服务器交换 + * @param newBody 新的请求体数据缓冲区 + * @return 新的ServerHttpRequest对象 + */ + private ServerHttpRequest createNewRequest(ServerWebExchange exchange, DataBuffer newBody) { + return new ServerHttpRequestDecorator(exchange.getRequest()) { + @Override + public Flux getBody() { + return Flux.just(newBody); + } + + @Override + public HttpHeaders getHeaders() { + HttpHeaders headers = new HttpHeaders(); + headers.putAll(exchange.getRequest().getHeaders()); + headers.remove(HttpHeaders.CONTENT_LENGTH); + headers.setContentLength(newBody.readableByteCount()); + return headers; + } + }; + } + + /** + * 处理url后拼接的参数 + * + * @param exchange 当前服务器交换 + * @param chain 提供委托给下一个过滤器的方式 + * @return {@code Mono} 表示请求处理完成的指示 + */ + private Mono handleUrlParams(ServerWebExchange exchange, GatewayFilterChain chain) { + try { + ServerWebExchange updatedExchange = updateRequestParam(exchange); + if (updatedExchange != null) { + return chain.filter(updatedExchange); + } else { + return chain.filter(exchange); + } + } catch (Exception e) { + log.error("处理 GET 请求时发生错误: {}", e.getMessage(), e); + exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); + return exchange.getResponse().setComplete(); + } + } + + /** + * 更新查询参数,解密和验证数据完整性 + * + * @param exchange 当前服务器交换 + * @return 更新后的ServerWebExchange对象,如果无需更新则返回null + */ + private ServerWebExchange updateRequestParam(ServerWebExchange exchange) { + ServerHttpRequest request = exchange.getRequest(); + boolean integrality = "true".equalsIgnoreCase(request.getHeaders().getFirst(INTEGRALITY)); + boolean encrypt = "true".equalsIgnoreCase(request.getHeaders().getFirst(ENCRYPT)); + URI uri = request.getURI(); + String query = uri.getQuery(); + + if (!ObjectUtils.isEmpty(query)) { + // 解密查询参数 + if (encryptRequest && encrypt) { + try { + query = AesCbcUtils.decrypt(query); + } catch (Exception e) { + log.error("解密查询参数时发生错误: {}", e.getMessage(), e); + throw new CaptchaException("请求参数不正确"); + } + } + + // 校验数据完整性 + if (integrity && integrality) { + String providedHmac = exchange.getRequest().getHeaders().getFirst(HMAC_HEADER_NAME); + integrityVerification(providedHmac, query); + } + + if (ObjectUtils.isEmpty(query)) { + return null; + } + + // 更新查询参数 + Map> queryParams = Arrays.stream(query.split("&")) + .map(param -> param.split("=")) + .collect(Collectors.toMap(param -> param[0], param -> Collections.singletonList(param[1]))); + URI newUri = UriComponentsBuilder.fromUri(uri) + .replaceQueryParams(CollectionUtils.toMultiValueMap(queryParams)) + .build(true) + .toUri(); + + ServerHttpRequest newRequest = request.mutate().uri(newUri).build(); + return exchange.mutate().request(newRequest).build(); + } + return null; + } + + /** + * 数据完整性校验 + * + * @param providedHmac 请求头中的 HMAC 值 + * @param query 请求参数 + */ + private void integrityVerification(String providedHmac, String query) { + if (providedHmac == null) { + log.error("请求头中缺少 Params-Hash"); + throw new CaptchaException("请求参数不正确"); + } + String encrypt = Sm3Util.encrypt(query); + log.debug("加密后的参数: {}", encrypt); + log.debug("请求头中的 Params-Hash: {}", providedHmac); + if (!encrypt.equals(providedHmac)) { + log.error("参数校验失败"); + throw new CaptchaException("请求参数不正确"); } - log.debug("[GatewayContext]ContentType:{},Gateway context is set with {}", contentType, gatewayContext); - return chain.filter(exchange); } } diff --git a/bonus-gateway/src/main/java/com/bonus/gateway/filter/ResponseEncryptFilter.java b/bonus-gateway/src/main/java/com/bonus/gateway/filter/ResponseEncryptFilter.java index be0a0a1..2751391 100644 --- a/bonus-gateway/src/main/java/com/bonus/gateway/filter/ResponseEncryptFilter.java +++ b/bonus-gateway/src/main/java/com/bonus/gateway/filter/ResponseEncryptFilter.java @@ -1,11 +1,7 @@ package com.bonus.gateway.filter; -import com.alibaba.fastjson.JSON; -import com.alibaba.nacos.shaded.com.google.common.collect.Maps; import com.bonus.common.core.utils.encryption.AesCbcUtils; -import com.bonus.common.core.utils.global.SystemGlobal; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.compress.utils.Charsets; import org.reactivestreams.Publisher; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.gateway.filter.GatewayFilterChain; @@ -26,11 +22,7 @@ import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.net.URI; -import java.util.Map; -import java.util.Objects; - -import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; +import java.nio.charset.StandardCharsets; /** * 对返回的 data数据进行加密 @@ -41,105 +33,89 @@ import static org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE; @Slf4j public class ResponseEncryptFilter implements GlobalFilter, Ordered { - @Value("${system.encryptEnabled}") - public boolean encryptEnabled; - /** - * 返回的数据 是否加密 - */ - public final static String KEY_HEAD = "decrypt"; + @Value("${system.encryptResponse}") + private boolean encryptResponse; + + private static final String ENCRYPT_RESPONSE = "data_encrypt_response"; @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - log.info("============================ResponseEncryptFilter start==================================="); - ServerHttpRequest request = exchange.getRequest(); - URI uri = request.getURI(); - HttpHeaders headers = request.getHeaders(); - if (headers != null) { - Object object = headers.getFirst("Content-Type"); - Object head = headers.getFirst(SystemGlobal.KEY_ENCRYPT); - if (head != null) { - String keyHead = head.toString(); - if (SystemGlobal.KEY_ENCRYPT.equals(keyHead)) { - return chain.filter(exchange); - } - } - if (object != null) { - String contentType = object.toString(); - if (contentType.contains(MULTIPART_FORM_DATA_VALUE)) { - return chain.filter(exchange); - } - } - } - HttpStatus statusCode = exchange.getResponse().getStatusCode(); - if (Objects.equals(statusCode, HttpStatus.BAD_REQUEST) || Objects.equals(statusCode, HttpStatus.TOO_MANY_REQUESTS)) { - // 如果是特殊的请求,已处理响应内容,这里不再处理 - return chain.filter(exchange); - } - //是否加密 - if (!encryptEnabled) { - return chain.filter(exchange); - } - // 根据具体业务内容,修改响应体 - return modifyResponseBody(exchange, chain); - } - /** - * 修改响应体 - * - * @param exchange - * @param chain - * @return - */ - private Mono modifyResponseBody(ServerWebExchange exchange, GatewayFilterChain chain) { - ServerHttpResponse originalResponse = exchange.getResponse(); - originalResponse.getHeaders().setContentType(MediaType.APPLICATION_JSON); - DataBufferFactory bufferFactory = originalResponse.bufferFactory(); - ServerHttpResponseDecorator response = buildResponse(originalResponse, bufferFactory); - return chain.filter(exchange.mutate().response(response).build()); - } + // 检查请求头中是否包含加密标志,并且系统是否启用了加密功能 + if (shouldEncrypt(headers)) { + ServerHttpResponse originalResponse = exchange.getResponse(); + DataBufferFactory bufferFactory = originalResponse.bufferFactory(); + // 设置响应头 + addResponseHeaders(originalResponse, headers); + // 创建自定义的响应装饰器,用于处理响应数据 + ServerHttpResponseDecorator responseDecorator = buildResponse(originalResponse, bufferFactory); + // 使用装饰器包装后的响应继续处理链 + return chain.filter(exchange.mutate().response(responseDecorator).build()); + } + // 如果不需要加密,直接继续处理链 + return chain.filter(exchange); + } @Override public int getOrder() { return -5; } - @SuppressWarnings("deprecation") + /** + * 判断是否需要对响应数据进行加密 + * + * @param headers 请求头 + * @return 如果需要加密返回true,否则返回false + */ + private boolean shouldEncrypt(HttpHeaders headers) { + return encryptResponse && Boolean.parseBoolean(headers.getFirst(ENCRYPT_RESPONSE)); + } + + /** + * 添加自定义的响应头 + * + * @param response 原始响应对象 + * @param requestHeaders 请求头 + */ + private void addResponseHeaders(ServerHttpResponse response, HttpHeaders requestHeaders) { + response.getHeaders().setContentType(MediaType.APPLICATION_JSON); + response.getHeaders().add(ENCRYPT_RESPONSE, requestHeaders.getFirst(ENCRYPT_RESPONSE)); + } + + /** + * 创建自定义的响应装饰器,用于加密响应数据 + * + * @param originalResponse 原始响应对象 + * @param bufferFactory 数据缓冲区工厂 + * @return 自定义的响应装饰器 + */ private ServerHttpResponseDecorator buildResponse(ServerHttpResponse originalResponse, DataBufferFactory bufferFactory) { return new ServerHttpResponseDecorator(originalResponse) { @Override public Mono writeWith(Publisher body) { + // 只处理状态码为200 OK的响应,并且响应体是Flux类型 if (getStatusCode().equals(HttpStatus.OK) && body instanceof Flux) { Flux fluxBody = Flux.from(body); + + // 对响应体进行加密处理 return super.writeWith(fluxBody.buffer().map(dataBuffers -> { - DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); - DataBuffer join = dataBufferFactory.join(dataBuffers); - byte[] content = new byte[join.readableByteCount()]; - join.read(content); - DataBufferUtils.release(join); - // 流转为字符串 - String responseData = new String(content, Charsets.UTF_8); - System.out.println(responseData); - Map map = JSON.parseObject(responseData); - Object encrypt = map.get(SystemGlobal.KEY_DECRYPT); - Map maps = Maps.newHashMap(); - if (encrypt == null || encrypt == "" || SystemGlobal.TRUE_STR.equals(encrypt)) { - responseData = AesCbcUtils.encrypt(JSON.toJSONString(map)); - maps.put("data", responseData); - maps.put(SystemGlobal.KEY_DECRYPT, true); - responseData = JSON.toJSONString(maps); - } else { - maps.put("data", responseData); - maps.put(SystemGlobal.KEY_DECRYPT, false); - } - byte[] uppedContent = responseData.getBytes(Charsets.UTF_8); - originalResponse.getHeaders().setContentLength(uppedContent.length); - return bufferFactory.wrap(uppedContent); + DataBuffer joinedBuffer = joinDataBuffers(dataBuffers); + byte[] content = readContent(joinedBuffer); + DataBufferUtils.release(joinedBuffer); + + // 将响应数据加密 + String responseData = new String(content, StandardCharsets.UTF_8); + responseData = AesCbcUtils.encrypt(responseData); + byte[] encryptedContent = responseData.getBytes(StandardCharsets.UTF_8); + // 设置加密后的内容长度 + originalResponse.getHeaders().setContentLength(encryptedContent.length); + return bufferFactory.wrap(encryptedContent); })); } else { - log.error("获取响应体数据 :" + getStatusCode()); + log.error("Failed to retrieve response body. Status code: {}", getStatusCode()); } return super.writeWith(body); } @@ -150,5 +126,31 @@ public class ResponseEncryptFilter implements GlobalFilter, Ordered { } }; } + + /** + * 将多个DataBuffer连接成一个DataBuffer + * + * @param dataBuffers DataBuffer列表 + * @return 连接后的DataBuffer + */ + private DataBuffer joinDataBuffers(java.util.List dataBuffers) { + if (dataBuffers.size() > 1) { + return new DefaultDataBufferFactory().join(dataBuffers); + } else { + return dataBuffers.get(0); + } + } + + /** + * 从DataBuffer中读取字节数组内容 + * + * @param dataBuffer DataBuffer对象 + * @return 读取到的字节数组 + */ + private byte[] readContent(DataBuffer dataBuffer) { + byte[] content = new byte[dataBuffer.readableByteCount()]; + dataBuffer.read(content); + return content; + } } diff --git a/bonus-gateway/src/main/java/com/bonus/gateway/filter/ValidateCodeFilter.java b/bonus-gateway/src/main/java/com/bonus/gateway/filter/ValidateCodeFilter.java index 344080d..03022b8 100644 --- a/bonus-gateway/src/main/java/com/bonus/gateway/filter/ValidateCodeFilter.java +++ b/bonus-gateway/src/main/java/com/bonus/gateway/filter/ValidateCodeFilter.java @@ -1,30 +1,25 @@ package com.bonus.gateway.filter; -import java.nio.CharBuffer; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.atomic.AtomicReference; - -import com.bonus.common.core.domain.R; -import com.bonus.common.core.exception.CaptchaException; -import com.bonus.common.core.utils.encryption.AesCbcUtils; -import org.apache.commons.lang3.ObjectUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.cloud.gateway.filter.GatewayFilter; -import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.core.io.buffer.DataBufferUtils; -import org.springframework.http.HttpHeaders; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.stereotype.Component; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONObject; +import com.bonus.common.core.exception.CaptchaException; import com.bonus.common.core.utils.ServletUtils; import com.bonus.common.core.utils.StringUtils; import com.bonus.gateway.config.properties.CaptchaProperties; import com.bonus.gateway.service.ValidateCodeService; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; + +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.atomic.AtomicReference; /** * 验证码过滤器 @@ -35,8 +30,6 @@ import reactor.core.publisher.Mono; public class ValidateCodeFilter extends AbstractGatewayFilterFactory { private final static String[] VALIDATE_URL = new String[]{"/auth/login", "/auth/register", "/auth/getPhoneCode"}; - @Value("${system.decryptEnabled}") - public boolean jaData; @Autowired private ValidateCodeService validateCodeService; @@ -59,9 +52,6 @@ public class ValidateCodeFilter extends AbstractGatewayFilterFactory { } try { String rspStr = resolveBodyFromRequest(request); - if (jaData) { - rspStr = AesCbcUtils.decrypt(rspStr); - } if (StringUtils.isEmpty(rspStr)) { throw new CaptchaException("请求参数异常"); } @@ -73,7 +63,7 @@ public class ValidateCodeFilter extends AbstractGatewayFilterFactory { } } catch (Exception e) { - return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage(), jaData); + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage(), true); } return chain.filter(exchange); }; diff --git a/bonus-gateway/src/main/java/com/bonus/gateway/handler/GatewayExceptionHandler.java b/bonus-gateway/src/main/java/com/bonus/gateway/handler/GatewayExceptionHandler.java index e31c75c..e8500fe 100644 --- a/bonus-gateway/src/main/java/com/bonus/gateway/handler/GatewayExceptionHandler.java +++ b/bonus-gateway/src/main/java/com/bonus/gateway/handler/GatewayExceptionHandler.java @@ -22,8 +22,6 @@ import reactor.core.publisher.Mono; @Configuration public class GatewayExceptionHandler implements ErrorWebExceptionHandler { - @Value("${system.encryptEnabled}") - public boolean encryptEnabled; private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class); @@ -55,6 +53,6 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); - return ServletUtils.webFluxResponseWriter(response, msg, encryptEnabled); + return ServletUtils.webFluxResponseWriter(response, msg, true); } } \ No newline at end of file diff --git a/bonus-gateway/src/main/java/com/bonus/gateway/handler/SentinelFallbackHandler.java b/bonus-gateway/src/main/java/com/bonus/gateway/handler/SentinelFallbackHandler.java index 1118d91..dcc02bc 100644 --- a/bonus-gateway/src/main/java/com/bonus/gateway/handler/SentinelFallbackHandler.java +++ b/bonus-gateway/src/main/java/com/bonus/gateway/handler/SentinelFallbackHandler.java @@ -16,12 +16,10 @@ import reactor.core.publisher.Mono; */ public class SentinelFallbackHandler implements WebExceptionHandler { - @Value("${system.encryptEnabled}") - public boolean encryptEnabled; private Mono writeResponse(ServerResponse response, ServerWebExchange exchange) { - return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试",encryptEnabled); + return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试",true); } @Override