五个常见的电子商务网站,网络营销平台的类型,合肥百度搜索优化,做曖視頻网站文章目录 前言正文一、项目简介二、核心代码2.1 自定义过滤器2.2 网关配置2.3 自定义配置类2.4 加密组件接口2.5 加密组件实现#xff0c;AES算法2.6 启动类#xff0c;校验支持的算法配置 三、请求报文示例四、测试结果4.1 网关项目启动时4.2 发生请求时 前言 本文环境使用比… 文章目录 前言正文一、项目简介二、核心代码2.1 自定义过滤器2.2 网关配置2.3 自定义配置类2.4 加密组件接口2.5 加密组件实现AES算法2.6 启动类校验支持的算法配置 三、请求报文示例四、测试结果4.1 网关项目启动时4.2 发生请求时 前言 本文环境使用比较新的 Java 17 和 SpringBoot 3.1.5对应到Spring的版本是 6.0.13 使用到的三方插件有 lombokgsonhutool 本文注重实现请求的解密和响应的加密加解密使用的是 Hutool 中的工具类加解密算法目前提供了AES的方式其余方式也可兼容扩展。 完整代码仓库https://gitee.com/fengsoshuai/springcloud-gateway-feng-demo
借用网关中的过滤器GlobalFilter来实现这一功能。 本文只粘贴一些重点文件内容。
正文
一、项目简介 在聚合项目中有两个核心模块feng-server提供了 rest 接口供网关使用。 feng-gateway 是核心实现的网关项目实现了自定义过滤器以及增加了一些基本配置功能。本文重心是网关项目。
二、核心代码
2.1 自定义过滤器
package org.feng.fenggateway.filters;import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.feng.fenggateway.config.SecureProperties;
import org.feng.fenggateway.dto.ResponseDto;
import org.feng.fenggateway.secure.SecureComponent;
import org.feng.fenggateway.secure.SecureComponentFactory;
import org.feng.fenggateway.util.GsonUtil;
import org.reactivestreams.Publisher;
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.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.Objects;
import java.util.Set;/*** 自定义密文过滤器** author feng*/
Slf4j
Component
public class CustomCipherTextFilter implements GlobalFilter, Ordered {Resourceprivate SecureProperties secureProperties;private SecureComponent secureComponent;Overridepublic MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 获取请求体ServerHttpRequest request exchange.getRequest();// 获取响应体ServerHttpResponse response exchange.getResponse();// 请求头HttpHeaders headers request.getHeaders();// 请求方法HttpMethod method request.getMethod();// 满足条件进行过滤if (isNeedFilterMethod(method) isNeedFilterContentType(headers.getContentType())) {return DataBufferUtils.join(request.getBody()).flatMap(dataBuffer - {try {// 获取请求参数String originalRequestBody getOriginalRequestBody(dataBuffer);// 解密请求参数String decryptRequestBody decryptRequest(originalRequestBody);// 装饰新的请求体ServerHttpRequestDecorator requestDecorator serverHttpRequestDecorator(request, decryptRequestBody);// 装饰新的响应体ServerHttpResponseDecorator responseDecorator serverHttpResponseDecorator(response);// 使用新的请求和响应转发ServerWebExchange serverWebExchange exchange.mutate().request(requestDecorator).response(responseDecorator).build();// 放行拦截return chain.filter(serverWebExchange);} catch (Exception e) {log.error(密文过滤器加解密错误, e);return Mono.empty();} finally {DataBufferUtils.release(dataBuffer);}});}return chain.filter(exchange);}private String decryptRequest(String originalRequestBody) {if (!secureProperties.enableDecryptRequestParam()) {log.info(请求参数解密跳过);return originalRequestBody;}log.info(请求参数解密原文{}, originalRequestBody);String decrypted getSecureComponent().decrypt(originalRequestBody);log.info(请求参数解密明文{}, decrypted);return decrypted;}private String encryptResponse(String originalResponseBody) {if (!secureProperties.enableEncryptResponseParam()) {log.info(响应结果加密跳过);return originalResponseBody;}ResponseDto responseDto GsonUtil.fromJson(originalResponseBody, ResponseDto.class);// 只对data字段进行加密处理Object data responseDto.getData();if (Objects.nonNull(data)) {responseDto.setData(getSecureComponent().encrypt(data.toString()));}log.info(响应结果加密原文{}, originalResponseBody);String result GsonUtil.toJson(responseDto);log.info(响应结果加密密文{}, result);return result;}/*** 获取原始的请求参数** param dataBuffer 数据缓冲* return 原始的请求参数*/private String getOriginalRequestBody(DataBuffer dataBuffer) {byte[] bytes new byte[dataBuffer.readableByteCount()];dataBuffer.read(bytes);return new String(bytes, StandardCharsets.UTF_8);}private boolean isNeedFilterMethod(HttpMethod method) {return NEED_FILTER_METHOD_SET.contains(method);}private boolean isNeedFilterContentType(MediaType mediaType) {return NEED_FILTER_MEDIA_TYPE_SET.contains(mediaType) || json.equals(mediaType.getSubtype());}private ServerHttpRequestDecorator serverHttpRequestDecorator(ServerHttpRequest originalRequest, String decryptRequestBody) {return new ServerHttpRequestDecorator(originalRequest) {Overridepublic HttpHeaders getHeaders() {HttpHeaders httpHeaders new HttpHeaders();httpHeaders.putAll(super.getHeaders());httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, chunked);return httpHeaders;}Overridepublic FluxDataBuffer getBody() {byte[] bytes decryptRequestBody.getBytes(StandardCharsets.UTF_8);return Flux.just(new DefaultDataBufferFactory().wrap(bytes));}};}private ServerHttpResponseDecorator serverHttpResponseDecorator(ServerHttpResponse originalResponse) {DataBufferFactory dataBufferFactory originalResponse.bufferFactory();return new ServerHttpResponseDecorator(originalResponse) {Overridepublic MonoVoid writeWith(Publisher? extends DataBuffer body) {if (body instanceof Flux? extends DataBuffer fluxBody) {return super.writeWith(fluxBody.buffer().map(dataBuffers - {DataBuffer join dataBufferFactory.join(dataBuffers);byte[] byteArray new byte[join.readableByteCount()];join.read(byteArray);DataBufferUtils.release(join);String originalResponseBody new String(byteArray, StandardCharsets.UTF_8);//加密byte[] encryptedByteArray encryptResponse(originalResponseBody).getBytes(StandardCharsets.UTF_8);originalResponse.getHeaders().setContentLength(encryptedByteArray.length);originalResponse.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8);return dataBufferFactory.wrap(encryptedByteArray);}));}return super.writeWith(body);}Overridepublic MonoVoid 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 static final SetHttpMethod NEED_FILTER_METHOD_SET Set.of(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT);private static final SetMediaType NEED_FILTER_MEDIA_TYPE_SET Set.of(MediaType.APPLICATION_JSON);Overridepublic int getOrder() {return -1;}public SecureComponent getSecureComponent() {if (Objects.isNull(secureComponent)) {secureComponent SecureComponentFactory.get(secureProperties.getAlgorithm());}return secureComponent;}
}
2.2 网关配置
server:port: 10010 # 网关端口
spring:application:name: gateway # 服务名称cloud:gateway:routes: # 网关路由配置- id: feng-server1 # 路由id自定义只要唯一即可uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址predicates: # 路由断言也就是判断请求是否符合路由规则的条件- Path/server/list/server1/** # 这个是按照路径匹配只要以/user/开头就符合要求- id: feng-server2uri: http://127.0.0.1:8082predicates:- Path/server/list/server2/**# 自定义配置
feng:gateway:secure:request-switch:enable: falseresponse-switch:enable: truealgorithm: aes
2.3 自定义配置类
package org.feng.fenggateway.config;import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.feng.fenggateway.secure.SecureComponentFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;import java.util.Objects;/*** 加解密属性配置** author feng*/
Slf4j
Data
ConfigurationProperties(prefix SecureProperties.SECURE_PROPERTIES_PREFIX)
public class SecureProperties {public static final String SECURE_PROPERTIES_PREFIX feng.gateway.secure;/*** 算法*/private SymmetricAlgorithm algorithm;/*** 请求开关*/private SecureSwitch requestSwitch;/*** 响应开关*/private SecureSwitch responseSwitch;public void checkSupportedAlgorithm() {log.info(校验是否支持算法{}, algorithm);if (Objects.isNull(algorithm)) {return;}boolean supportedAlgorithm SecureComponentFactory.isSupportedAlgorithm(algorithm);if (!supportedAlgorithm) {throw new UnsupportedOperationException(不支持的算法);}log.info(校验是否支持算法校验通过);}/*** 是否启用解密请求参数** return 默认为否其他情况看配置*/public boolean enableDecryptRequestParam() {if (Objects.isNull(requestSwitch)) {return false;}return requestSwitch.getEnable();}/*** 是否启用加密响应参数** return 默认为否其他情况看配置*/public boolean enableEncryptResponseParam() {if (Objects.isNull(responseSwitch)) {return false;}return responseSwitch.getEnable();}
}
2.4 加密组件接口
这个可以用来扩展支持其他加密算法目前实现类只有AES。
package org.feng.fenggateway.secure;import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import jakarta.annotation.PostConstruct;/*** 加解密组件** author feng*/
public interface SecureComponent {/*** 加密** param originalText 原文* return 密文*/String encrypt(String originalText);/*** 解密** param encryptedText 密文* return 解密后的明文*/String decrypt(String encryptedText);/*** 获取加解密算法类型** return 加解密算法类型*/SymmetricAlgorithm getAlgorithmType();PostConstructdefault void registerToFactory() {SecureComponentFactory.registerBean(this);}
}
2.5 加密组件实现AES算法
package org.feng.fenggateway.secure;import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import org.springframework.stereotype.Component;import java.nio.charset.StandardCharsets;/*** AES加解密组件** author feng*/
Component
public class SecureAESComponent implements SecureComponent {/*** 生成密钥16、24、32位都行*/private final static byte[] SECURE_KEY r4oz0f3kfk5tgyui.getBytes(StandardCharsets.UTF_8);/*** 偏移量必须16位*/private final static String IV r21g95kdsd423gy6;private final static AES AES_INSTANCE new AES(Mode.CTS, Padding.PKCS5Padding, SECURE_KEY, IV.getBytes(StandardCharsets.UTF_8));Overridepublic String encrypt(String originalText) {return AES_INSTANCE.encryptHex(originalText);}Overridepublic String decrypt(String encryptedText) {return AES_INSTANCE.decryptStr(encryptedText);}Overridepublic SymmetricAlgorithm getAlgorithmType() {return SymmetricAlgorithm.AES;}
}
2.6 启动类校验支持的算法配置
package org.feng.fenggateway;import jakarta.annotation.Resource;
import org.feng.fenggateway.config.SecureProperties;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;ConfigurationPropertiesScan
SpringBootApplication
public class FengGatewayApplication implements CommandLineRunner {Resourceprivate SecureProperties secureProperties;public static void main(String[] args) {SpringApplication.run(FengGatewayApplication.class, args);}Overridepublic void run(String... args) {secureProperties.checkSupportedAlgorithm();}
}
三、请求报文示例
POST http://localhost:10010/server/list/server2/user?authorizationfeng
Content-Type: application/json;charsetUTF-8{username: fbb
}四、测试结果
4.1 网关项目启动时
校验结果正常
4.2 发生请求时
可以看到data字段已经加密响应了。
请求和响应结果