在项目中对携带敏感信息的接口进行加解密
1、创建一个切面类
PassAspec
package com.XXX.XXX.XXX.XXX.aspect;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.jinmdz.fmis.api.pass.PassApi;
import com.jinmdz.fmis.common.util.EmptyUtil;
import com.jinmdz.fmis.core.base.BaseResult;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
/**
* @Author:caohuohuo
* @Date:2024/8/30 16:13
* @Filename:PassAspec
*/
@Aspect //定义一个切面
@Component //让Spring能够在应用程序启动时自动扫描并加载此切面
@Slf4j //Logger 实例并记录日志
public class PassAspec {
private static final String DEFAULT_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
private static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
@Resource
private PassApi passApi;//这里调用外部加解密接口API,可以根据自己的实际情况去加解密
// 定义切点,监听使用此注解的方法
@Pointcut("@annotation(com.XXX.XXX.XXX.XXX.XXX.PassAnnotation)")
public void pointCut() {
}
//环绕通知
//调用接口调用前进行解密处理
@Around("pointCut()")
public Object getCapCode(ProceedingJoinPoint joinPoint) {
try {
Object[] args = joinPoint.getArgs();
if (null != args) {
JSONObject inParamJSON = (JSONObject) JSONObject.toJSON(args[0]);
//解密 ciphertext字段为设置的加密参数,这里判断加密字段存在并且不为null时进行解密
if (inParamJSON.containsKey("ciphertext")&&EmptyUtil.isNotEmpty(inParamJSON.getString("ciphertext"))){
String ciphertext = inParamJSON.getString("ciphertext");
JSONObject inParam=new JSONObject();//组装解密报文
inParam.put("data",ciphertext);
JSONObject decryptJson = passApi.decrypt(inParam);//调用外部解密接口进行解密
if (EmptyUtil.isNotEmpty(decryptJson)){
if (decryptJson.containsKey("code")&&decryptJson.getInteger("code")==200){
args[0]=changObjectValue(args[0],decryptJson.getString("data"));//解密成功后进行类型转换,转换完重新赋值
}
}
}
}
return joinPoint.proceed(args);//调用相应方法,加密也可以在此之后进行加密,这里使用后置通知进行加密
} catch (Throwable e) {
log.error("目标方法执行异常,目标类:" + joinPoint.getTarget() + "方法:" + joinPoint.getSignature().getName(), e);
throw new RuntimeException("系统繁忙,请稍后再试!");
}
}
//后置通知
//接口返回之后对结果加密处理
@AfterReturning(value = "pointCut()",returning = "methodResult")//value:引用自定义的切点方法;returning:返回值
public Object afterReturningPublish(Object methodResult) {
//将返回值转成自己框架相应的实体用作返回
BaseResult result = (BaseResult) methodResult;
ObjectMapper objectMapper = new ObjectMapper();
//设置时间格式
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN)));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN)));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//设置时区
objectMapper.registerModule(javaTimeModule);
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
//加密信息
JSONObject inParam=new JSONObject();
try {//这里将返回值整体转成字符串进行加密
inParam.put("data", objectMapper.writeValueAsString(methodResult));
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException("系统繁忙,请稍后再试!");
}//调用外部加密接口进行加密,这里根据项目实际情况处理
JSONObject encryptJson = passApi.encrypt(inParam);
if (EmptyUtil.isNotEmpty(encryptJson)){
if (encryptJson.containsKey("code")&&encryptJson.getInteger("code")==200){//加密成功后将加密串返回
result.setData(encryptJson.getString("data"));
}else {
result.setCode(0);
result.setMessage("加密失败");
}
}
return result;
}
/***
* 修改入参具体字段值,这里需要修改请求体所有所以整体转换了,注意转换时的类型
*/
private Object changObjectValue(Object obj, String obj2) throws Exception {
return JSONObject.parseObject(obj2,obj.getClass());
}
}
2、注解类
PassAnnotation
package com.XXX.XXX.XXX.XXX.aspect;
import java.lang.annotation.*;
/**
* @Author:caohuohuo
* @Date:2024/8/30 16:25
* @Filename:PassAnnotation
*/
@Target(ElementType.METHOD) //注解放置的目标位置,METHOD是可注解在方法级别上
@Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
@Documented //使用注解的方法生成 Javadoc 时,当前注解 会被包括在内
public @interface PassAnnotation {
}
这里在需要使用加解密的接口上加上@PassAnnotation注解就OK了。