数据加密和完整性校验
This commit is contained in:
parent
a7d6d78297
commit
ada6ccb7a7
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue