AES256通过注解方式接口实现
上一篇通过方法的方式去实现接口aes的加密AES256加解密与异常处理,这一篇通过aop切面注解的方式去调用加密方法
方法实现
AesKeyConst密钥key64位
/**
* Copyright (C), 2018-2020
* FileName: AesKeyConst
* Author: Lee
* Date: 2020/11/3 10:44
* Description: aes256加密key
* History:
*/
package com.yc.asset.consts;
import lombok.Getter;
/**
* @author Lee
* @create 2020/11/3
* @since 1.0.0
*/
@Getter
public class AesKeyConst {
public static final String KEY = "3a6e2b4d4823f7c7c390c01ecd81d28c3a6e2b4d4823f7c7c390c01ecd81d28c";
}
DecryptResponse注解
/**
* Copyright (C), 2018-2020
* FileName: DecryptResponse
* Author: Lee
* Date: 2020/11/5 9:18
* Description: aes256加解密
* History:
*/
package com.yc.asset.framework.annotation;
import java.lang.annotation.*;
/**
* @author Lee
* @create 2020/11/5
* @since 1.0.0
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DecryptResponse {
//请求参是否需要加密,默认true
boolean inDecode() default true;
//出参是否需要加密,默认true
boolean outEncode() default true;
}
DecryptRequestBodyAdvice解密
/**
* Copyright (C), 2018-2020
* FileName: DecryptRequestBodyAdvice
* Author: Lee
* Date: 2020/11/5 9:19
* Description:
* History:
*/
package com.yc.asset.framework.advice;
import com.yc.asset.framework.annotation.DecryptResponse;
import com.yc.asset.utils.http.DecryptHttpInputMessage;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
/**
* @author Lee
* @create 2020/11/5
* @since 1.0.0
*/
@ControllerAdvice
public class DecryptRequestBodyAdvice implements RequestBodyAdvice {
//获取接口是否包含DecryptResponse
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return methodParameter.getMethod().isAnnotationPresent(DecryptResponse.class);
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
if (needDecrypt(parameter)) {
return new DecryptHttpInputMessage(inputMessage, StandardCharsets.UTF_8);
}
return inputMessage;
}
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
//判断当前接口请求是否需要解密
public boolean needDecrypt(MethodParameter returnType) {
boolean encrypt = false;
boolean classPresentAnno = returnType.getContainingClass().isAnnotationPresent(DecryptResponse.class);
boolean methodPresentAnno = returnType.getMethod().isAnnotationPresent(DecryptResponse.class);
if(classPresentAnno){
//类上标注的是否需要加密
encrypt = returnType.getContainingClass().getAnnotation(DecryptResponse.class).inDecode();
//类不加密,所有都不加密
if(!encrypt){
return false;
}
}
if(methodPresentAnno){
//方法上标注的是否需要加密
encrypt = returnType.getMethod().getAnnotation(DecryptResponse.class).inDecode();
}
return encrypt;
}
}
DecryptHttpInputMessage
/**
* Copyright (C), 2018-2020
* FileName: DecryptHttpInputMessage
* Author: Lee
* Date: 2020/11/5 9:33
* Description:
* History:
*/
package com.yc.asset.utils.http;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.alibaba.fastjson.JSON;
import com.yc.asset.consts.AesKeyConst;
import com.yc.asset.orms.vo.MonitorReqVO;
import com.yc.asset.orms.vo.MonitorRestMapVO;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
/**
* @author Lee
* @create 2020/11/5
* @since 1.0.0
*/
@AllArgsConstructor
public class DecryptHttpInputMessage implements HttpInputMessage {
private HttpInputMessage inputMessage;
private Charset charset;
@Override
public InputStream getBody() throws IOException {
String content = IoUtil.read(inputMessage.getBody(), charset);
//这个地方根据实际需要自定义类型进行处理,博主因只对json体内的body值进行加解密所以对出入参进行封装。
MonitorRestMapVO vo = JSON.parseObject(content, MonitorRestMapVO.class);
MonitorReqVO body = JSON.parseObject(vo.getBody().toString(), MonitorReqVO.class);
SecretKey secretKey = new SecretKeySpec(HexUtil.decodeHex(AesKeyConst.KEY), "AES");
AES aes = SecureUtil.aes(secretKey.getEncoded());
String decryptStr = aes.decryptStr(body.getVo());
body.setVo(decryptStr);
vo.setBody(body);
return new ByteArrayInputStream(JSON.toJSONString(vo).getBytes());
}
@Override
public HttpHeaders getHeaders() {
return inputMessage.getHeaders();
}
}
EncryptResponeBodyAdvice加密
/**
* Copyright (C), 2018-2020
* FileName: EncryptResponeBodyAdvice
* Author: Lee
* Date: 2020/11/5 16:07
* Description: 出参加密
* History:
*/
package com.yc.asset.framework.advice;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.yc.asset.consts.AesKeyConst;
import com.yc.asset.framework.annotation.DecryptResponse;
import com.yc.asset.orms.vo.ResultVersionTwoVO;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* @author Lee
* @create 2020/11/5
* @since 1.0.0
*/
@ControllerAdvice
public class EncryptResponeBodyAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return returnType.getMethod().isAnnotationPresent(DecryptResponse.class);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (null != body) {
DecryptResponse methodAnnotation = returnType.getMethodAnnotation(DecryptResponse.class);
if (methodAnnotation.outEncode()){
SecretKey secretKey = new SecretKeySpec(HexUtil.decodeHex(AesKeyConst.KEY), "AES");
AES aes = SecureUtil.aes(secretKey.getEncoded());
ResultVersionTwoVO vo = (ResultVersionTwoVO) body;
Object data = vo.getBody().getData();
String hex = aes.encryptHex(String.valueOf(data));
vo.getBody().setData(hex);
return vo;
}
}
return body;
}
}
MonitorRestMapVO
package com.yc.asset.orms.vo;
import com.yc.asset.framework.annotation.PreHeaderSignCheck;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 可以在POST METHOD中使用 {@link PreHeaderSignCheck} 进行header签名参数校验
*
* @author Lee
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MonitorRestMapVO<T> {
private RequestHeaderSignVO header;
private T body;
}
RequestHeaderSignVO
package com.yc.asset.orms.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Lee
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RequestHeaderSignVO {
private String timestamp;
private String username;
private String signmode;
private String type;
private String sign;
private String sync;
}
MonitorReqVO
/**
* Copyright (C), 2018-2020
* FileName: MonitorReqVO
* Author: Lee
* Date: 2020/11/11 18:02
* Description:
* History:
*/
package com.yc.asset.orms.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Lee
* @create 2020/11/11
* @since 1.0.0
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MonitorReqVO {
private String vo;
}
ResultVersionTwoVO
package com.yc.asset.orms.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author : Lee
* @version : 1.0
* @since : 2020/11/11 19:36
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ResultVersionTwoVO<H, B> {
// 返回头部
private H header;
// 返回数据体
private BodyVO<B> body;
}
BodyVO
package com.yc.asset.orms.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author : Lee
* @version : 1.0
* @since : 2020/11/11 19:36
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BodyVO<T> {
// 返回CODE
private String code;
// 返回错误信息
private String message;
// 返回数据
private T data;
}
接口示例
@PostMapping(value = "/getDemo", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
//@DecryptResponse(inDecode = false, outEncode = false)
@DecryptResponse
public ResultVersionTwoVO<HeaderVO, MonitorListVO<List<MonitorIdListPO>>> getDemo(@RequestBody MonitorRestMapVO<MonitorReqVO> request) {
RequestHeaderSignVO header = request.getHeader();
MonitorRestDataIdListVO body = JSON.parseObject(request.getBody().getVo(), MonitorRestDataIdListVO.class);
//具体实现逻辑
return new ResultVersionTwoVO<>(new HeaderVO(header.getType()), new BodyVO<>("1", "成功", new MonitorListVO<>(resultList)));
}
注意
因我们实现的是RequestBodyAdvice,所以请求在拦截时只会对接口中@RequestBody的参数进行处理