上篇介绍了多种加解密的使用java加密使用
本篇主要介绍在gateway网关中使用对参数解密和返回数据进行加密的操作
原理
下面使用的是AES加密 SHA1withRSA加签
1-用户使用拿到的AES秘钥和RSA私钥。对数据进行加密和加签
2-进行验签和时间的检验
3-将解密的数据返回到具体的调用方(通过特定的filter具体业务方是无感知加解密的)
4-将业务方数据加密返回给调用方
参数校验
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE + 10)
@Component
public class SignatureValidationGatewayFilterFactory extends AbstractGatewayFilterFactory {
private final EtrGatewayProperties etrGatewayProperties;
@Autowired
public SignatureValidationGatewayFilterFactory(EtrGatewayProperties etrGatewayProperties) {
this.etrGatewayProperties = etrGatewayProperties;
}
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
if (HttpMethod.GET.matches(exchange.getRequest().getMethodValue())
|| HttpMethod.POST.matches(exchange.getRequest().getMethodValue())) {
ModifyRequestBodyGatewayFilterFactory.Config modifyRequestConfig = new ModifyRequestBodyGatewayFilterFactory.Config()
.setContentType(ContentType.APPLICATION_JSON.getMimeType())
.setRewriteFunction(String.class, String.class, (exchange1, originalRequestBody) -> {
try {
JSONObject jsonObject = JSONUtil.parseObj(originalRequestBody);
String appId = jsonObject.getStr(SignatureConstants.SIGN_APPID);
String sign = jsonObject.getStr(SignatureConstants.SIGN_KEY);
String signType = jsonObject.getStr(SignatureConstants.SIGN_TYPE);
String timeStamp = jsonObject.getStr(SignatureConstants.SIGN_TIME_STAMP);
EtrGatewayProperties.SinatureConfig first = etrGatewayProperties.getSignatures().stream()
.filter(appConfig -> appConfig.getAppId().equalsIgnoreCase(appId))
.findFirst().orElse(null);
if (Objects.isNull(first)) {
log.error("appId:{}不合法", appId);
return Mono.error(BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR));
}
if (StrUtil.isBlank(sign) || StrUtil.isBlank(signType) || !StrUtil.isNumeric(timeStamp)) {
log.error("参数不合法:sign:{},signType:{},timestamp:{}", sign, signType, timeStamp);
return Mono.error(BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR));
}
// 验证时间戳
if (!validateTimestamp(timeStamp, first)) {
log.warn("Invalid timestamp for appId: {}", appId);
return Mono.error(BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR));
}
// 验证签名
if (!validateSignature(jsonObject, appId, signType, sign, first)) {
log.warn("Signature verification failed for appId: {}", appId);
return Mono.error(BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR));
}
String dataStr = decryptData(jsonObject.getStr(SignatureConstants.SIGN_DATA), first.getAesKey());
if (StringUtils.isBlank(dataStr)) {
return Mono.error(BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR));
}
exchange1.getRequest().mutate().header(SignatureConstants.SIGN_APPID, appId);
return Mono.just(dataStr);
} catch (Exception e) {
return Mono.error(e);
}
});
return new ModifyRequestBodyGatewayFilterFactory()
.apply(modifyRequestConfig)
.filter(exchange, chain)
.onErrorResume(e -> handleFilterError(exchange, e));
}
return chain.filter(exchange);
};
}
private boolean validateTimestamp(String timeStamp, EtrGatewayProperties.SinatureConfig appConfig) {
Integer timestampExpire = Optional.ofNullable(appConfig.getTimeStampExpire()).orElse(300);
DateTime time = DateUtil.parse(timeStamp, DateUtil.newSimpleFormat("yyyyMMddHHmmss"));
long between = DateUtil.between(time, new Date(), DateUnit.SECOND);
return Math.abs(between) <= timestampExpire;
}
private boolean validateSignature(JSONObject jsonObject, String appId, String signType, String sign, EtrGatewayProperties.SinatureConfig appConfig) {
String publicKey = appConfig.getPublicKey();
SignStrategy signStrategy = new DefaultSignStrategy();
try {
return signStrategy.verifyPost(jsonObject.getStr(SignatureConstants.SIGN_DATA), publicKey,
CipherType.valueOf(signType.toUpperCase()), sign);
} catch (Exception e) {
log.error("Signature verification failed for appId: {}", appId, e);
return false;
}
}
private String decryptData(String data, String aesKey) {
AES aes = SecureUtil.aes(aesKey.getBytes());
return aes.decryptStr(data);
}
private Mono<Void> handleFilterError(ServerWebExchange exchange, Throwable e) {
if (e instanceof BizException) {
log.error("Filter error: {}", e.getMessage());
return signatureError(exchange);
}
return Mono.error(e);
}
private Mono<Void> signatureError(ServerWebExchange exchange) {
log.warn("Signature error: {}", exchange.getRequest().getURI().getPath());
ApiResult<Object> result = ApiResult.fail(GatewayErrorCodeEnum.SIGNATURE_ERROR);
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
DataBuffer buffer = response.bufferFactory().wrap(JSONUtil.toJsonStr(result).getBytes());
return response.writeWith(Mono.just(buffer)).then(Mono.defer(response::setComplete));
}
}
返回参数加密
@Order(Ordered.HIGHEST_PRECEDENCE + 20)
@Component
public class SignatureResGatewayFilterFactory extends ModifyResponseBodyGatewayFilterFactory {
private final EtrGatewayProperties etrGatewayProperties;
public SignatureResGatewayFilterFactory(ServerCodecConfigurer codecConfigurer, Set<MessageBodyDecoder> bodyDecoders,
Set<MessageBodyEncoder> bodyEncoders, EtrGatewayProperties etrGatewayProperties) {
super(codecConfigurer.getReaders(), bodyDecoders, bodyEncoders);
this.etrGatewayProperties = etrGatewayProperties;
}
@Override
public GatewayFilter apply(Config config) {
config.setRewriteFunction(String.class, String.class, (serverWebExchange, s) -> {
JSONObject resJson = JSONUtil.parseObj(s);
Integer code = resJson.getInt("code");
if (200 == code) {
String dataStr = resJson.getStr("data");
if (StrUtil.isNotBlank(dataStr)) {
String appId = serverWebExchange.getRequest().getHeaders().getFirst(SignatureConstants.SIGN_APPID);
if (StrUtil.isBlank(appId)) {
return Mono.error(BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR));
}
AES aes = SecureUtil.aes(getAesKey(appId).getBytes());
resJson.set("data", aes.encryptBase64(dataStr));
return Mono.just(resJson.toString());
}
}
return Mono.just(s);
});
ModifyResponseGatewayFilter gatewayFilter = new ModifyResponseGatewayFilter(config);
gatewayFilter.setFactory(this);
return gatewayFilter;
}
private String getAesKey(String appId) {
EtrGatewayProperties.SinatureConfig first = etrGatewayProperties.getSignatures().stream()
.filter(appConfig -> appConfig.getAppId().equalsIgnoreCase(appId))
.findFirst().orElse(null);
if (Objects.isNull(first)) {
throw BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR);
}
String aesKey = first.getAesKey();
if (StrUtil.isBlank(aesKey)) {
throw BizException.of(GatewayErrorCodeEnum.SIGNATURE_ERROR);
}
return aesKey;
}
}