数据加密和完整性校验

This commit is contained in:
jiang 2024-08-07 11:27:01 +08:00
parent a7d6d78297
commit ada6ccb7a7
7 changed files with 91 additions and 38 deletions

View File

@ -1,15 +1,5 @@
package com.bonus.gateway.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.bonus.common.core.constant.CacheConstants;
import com.bonus.common.core.constant.HttpStatus;
import com.bonus.common.core.constant.SecurityConstants;
@ -20,10 +10,17 @@ import com.bonus.common.core.utils.StringUtils;
import com.bonus.common.redis.service.RedisService;
import com.bonus.gateway.config.properties.IgnoreWhiteProperties;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.concurrent.TimeUnit;
/**
* 网关鉴权
*
@ -115,7 +112,7 @@ public class AuthFilter implements GlobalFilter, Ordered {
private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) {
log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED, true);
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED);
}
/**

View File

@ -25,7 +25,7 @@ public class BlackListUrlFilter extends AbstractGatewayFilterFactory<BlackListUr
String url = exchange.getRequest().getURI().getPath();
if (config.matchBlacklist(url)) {
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求地址不允许访问", true);
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求地址不允许访问");
}
return chain.filter(exchange);

View File

@ -44,23 +44,51 @@ import java.util.stream.Collectors;
@Slf4j
public class RequestCoverFilter implements GlobalFilter, Ordered {
/**
* 是否启用解密功能
* 密钥算法
*/
@Value("${aesCbc.keyAlgorithm}")
public String keyAlgorithm;
/**
* 加密/解密算法 / 工作模式 / 填充方式
* Java 6支持PKCS5Padding填充方式
* Bouncy Castle支持PKCS7Padding填充方式
*/
@Value("${aesCbc.cipherAlgorithm}")
public String cipherAlgorithm;
/**
* 偏移量只有CBC模式才需要
*/
@Value("${aesCbc.ivParameter}")
public String ivParameter;
/**
* AES要求密钥长度为128位或192位或256位java默认限制AES密钥长度最多128位
*/
@Value("${aesCbc.sKey}")
public String sKey;
/**
* 编码格式导出
*/
@Value("${aesCbc.encoding}")
public String encoding;
/* *//**
* 是否启用解密功能
*//*
@Value("${system.encryptRequest}")
private boolean encryptRequest;
/**
*//**
* 是否启用数据完整性校验功能
*/
*//*
@Value("${system.integrity}")
private boolean integrity;
private boolean integrity;*/
/**
* 数据加密标志
*/
public static final String ENCRYPT = "data_encrypt_request";
public static final String ENCRYPT = "encryptRequest";
/**
* 数据完整性校验标志
*/
public static final String INTEGRALITY = "data_integrity";
public static final String INTEGRALITY = "checkIntegrity";
/**
* 完整性校验哈希值
*/
@ -76,9 +104,6 @@ public class RequestCoverFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 如果解密和完整性校验均未启用则直接通过过滤链
if (!encryptRequest && !integrity) {
return chain.filter(exchange);
}
ServerHttpRequest request = exchange.getRequest();
MediaType contentType = request.getHeaders().getContentType();
if (contentType == null) {
@ -123,9 +148,9 @@ public class RequestCoverFilter implements GlobalFilter, Ordered {
return exchange.getResponse().setComplete();
}
// 解密请求体
if (encryptRequest && encrypt) {
if ( encrypt) {
try {
requestBody = AesCbcUtils.decrypt(requestBody);
requestBody = AesCbcUtils.decrypt(requestBody, sKey, encoding, ivParameter, cipherAlgorithm, keyAlgorithm);
} catch (Exception e) {
log.error("解密请求体时发生错误: {}", e.getMessage(), e);
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
@ -134,7 +159,7 @@ public class RequestCoverFilter implements GlobalFilter, Ordered {
}
// 校验数据完整性
if (integrity && integrality) {
if (integrality) {
String providedHmac = exchange.getRequest().getHeaders().getFirst(HMAC_HEADER_NAME);
integrityVerification(providedHmac, requestBody);
}
@ -214,9 +239,9 @@ public class RequestCoverFilter implements GlobalFilter, Ordered {
if (!ObjectUtils.isEmpty(query)) {
// 解密查询参数
if (encryptRequest && encrypt) {
if ( encrypt) {
try {
query = AesCbcUtils.decrypt(query);
query = AesCbcUtils.decrypt(query, sKey, encoding, ivParameter, cipherAlgorithm, keyAlgorithm);
} catch (Exception e) {
log.error("解密查询参数时发生错误: {}", e.getMessage(), e);
throw new CaptchaException("请求参数不正确");
@ -224,7 +249,7 @@ public class RequestCoverFilter implements GlobalFilter, Ordered {
}
// 校验数据完整性
if (integrity && integrality) {
if (integrality) {
String providedHmac = exchange.getRequest().getHeaders().getFirst(HMAC_HEADER_NAME);
integrityVerification(providedHmac, query);
}

View File

@ -32,11 +32,43 @@ import java.nio.charset.StandardCharsets;
@Configuration
@Slf4j
public class ResponseEncryptFilter implements GlobalFilter, Ordered {
/**
* 密钥算法
*/
@Value("${aesCbc.keyAlgorithm}")
public String keyAlgorithm;
/**
* 加密/解密算法 / 工作模式 / 填充方式
* Java 6支持PKCS5Padding填充方式
* Bouncy Castle支持PKCS7Padding填充方式
*/
@Value("${aesCbc.cipherAlgorithm}")
public String cipherAlgorithm;
/**
* 偏移量只有CBC模式才需要
*/
@Value("${aesCbc.ivParameter}")
public String ivParameter;
/**
* AES要求密钥长度为128位或192位或256位java默认限制AES密钥长度最多128位
*/
@Value("${aesCbc.sKey}")
public String sKey;
/**
* 编码格式导出
*/
@Value("${aesCbc.encoding}")
public String encoding;
/* *//**
* 返回数据是否加密
*//*
@Value("${system.encryptResponse}")
private boolean encryptResponse;
private static final String ENCRYPT_RESPONSE = "data_encrypt_response";
private boolean encryptResponse;*/
/**
* 加密标识
*/
private static final String ENCRYPT_RESPONSE = "encryptResponse";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
@ -71,7 +103,7 @@ public class ResponseEncryptFilter implements GlobalFilter, Ordered {
* @return 如果需要加密返回true否则返回false
*/
private boolean shouldEncrypt(HttpHeaders headers) {
return encryptResponse && Boolean.parseBoolean(headers.getFirst(ENCRYPT_RESPONSE));
return Boolean.parseBoolean(headers.getFirst(ENCRYPT_RESPONSE));
}
/**
@ -108,7 +140,7 @@ public class ResponseEncryptFilter implements GlobalFilter, Ordered {
// 将响应数据加密
String responseData = new String(content, StandardCharsets.UTF_8);
responseData = AesCbcUtils.encrypt(responseData);
responseData = AesCbcUtils.encrypt(responseData, sKey, encoding, ivParameter, cipherAlgorithm, keyAlgorithm);
byte[] encryptedContent = responseData.getBytes(StandardCharsets.UTF_8);
// 设置加密后的内容长度
originalResponse.getHeaders().setContentLength(encryptedContent.length);

View File

@ -63,7 +63,7 @@ public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object> {
}
} catch (Exception e) {
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage(), true);
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage());
}
return chain.filter(exchange);
};

View File

@ -53,6 +53,6 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler
log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage());
return ServletUtils.webFluxResponseWriter(response, msg, true);
return ServletUtils.webFluxResponseWriter(response, msg);
}
}

View File

@ -3,7 +3,6 @@ package com.bonus.gateway.handler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.bonus.common.core.utils.ServletUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebExceptionHandler;
@ -19,7 +18,7 @@ public class SentinelFallbackHandler implements WebExceptionHandler
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange)
{
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试",true);
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试");
}
@Override