SpringBoot参数加解密
概述
- 有时候,为了接口安全,防止接口数据被拦截抓取,我们需要对请求,响应参数进行加密。接口
org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice
能对请求参数进行前置处理,而org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice
对响应结果数据进行后置处理。因此这两个接口能很好的植入我们的加解密代码段,统一进行全局操作,避免重复编码。
实现流程
1.添加拦截注解
@Target({
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface SafetyProcess {
boolean decode() default true;
boolean encode() default true;
}
2.添加配置application.properties
# 注意:本项目使用ase算法,密钥长度固定为16字节
safety.secret=1234567890abcdef
3.实现前置解密处理逻辑
@ControllerAdvice
public class MyRequestBodyAdvice implements RequestBodyAdvice {
@Value("${safety.secret}")
private String secret;
@Override
public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
SafetyProcess process = methodParameter.getMethodAnnotation(SafetyProcess.class);
return null != process && process.decode();
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
HttpHeaders headers = httpInputMessage.getHeaders();
String bodyStr = StreamUtils.copyToString(httpInputMessage.getBody(), Charset.forName("utf-8"));
byte[] bytes = Base64.getDecoder().decode(bodyStr);
byte[] body = SecureUtil.aes(secret.getBytes()).decrypt(bytes);
return new MyHttpInputMessage(headers, body);
}
@Override
public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return o;
}
@Override
public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
return o;
}
@AllArgsConstructor
public static class MyHttpInputMessage implements HttpInputMessage {
private HttpHeaders headers;
private byte[] body;
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream(body);
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
}
}
4.实现后置加密处理逻辑
@ControllerAdvice
@Slf4j
public class MyResponseBodyAdvice implements ResponseBodyAdvice {
@Value("${safety.secret}")
private String secret;
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
SafetyProcess process = methodParameter.getMethodAnnotation(SafetyProcess.class);
return null != process && process.encode();
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
Object data = null;
if (o instanceof Response) {
Response res = (Response) o;
if (res.isOk()) {
data = res.getData();
} else {
return o;
}
} else {
data = o;
}
if (null != data) {
ObjectMapper objectMapper = new ObjectMapper();
try {
String bodyStr = objectMapper.writeValueAsString(o);
byte[] bytes = SecureUtil.aes(secret.getBytes()).encrypt(bodyStr);
return Response.success(Base64.getEncoder().encodeToString(bytes));
} catch (JsonProcessingException e) {
log.error("数据加密异常", e);
}
}
return o;
}
}
5.测试代码
@Data
@Accessors(chain = true)
public class DemoReqDTO implements Serializable {
private static final long serialVersionUID = 1019466745376831818L;
private Integer a;
private String b;
}
@Data
@Accessors(chain = true)
public class DemoRespDTO implements Serializable {
private static final long serialVersionUID = 1019466745376831818L;
private Integer c;
private String d;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Response<T> implements Serializable {
private static final long serialVersionUID = 4921114729569667431L;
private Integer code;
private String message;
private T data;
public static final int SUCCESS = 200;
public static final int ERROR = 1000;
public static <R> Response<R> success(R data) {
return new Response<>(SUCCESS, "success", data);
}
public static <R> Response<R> error(String msg) {
return new Response<>(ERROR, msg, null);
}
@JsonIgnore
public boolean isOk() {
return null != getCode() && SUCCESS == getCode();
}
}
@RestController
public class DemoController {
@SafetyProcess
@PostMapping(value = "/test")
public Response<DemoRespDTO> test(@RequestBody DemoReqDTO reqDTO) {
DemoRespDTO respDTO = new DemoRespDTO().setC(3).setD("4");
return Response.success(respDTO);
}
@SafetyProcess(encode = false)
@PostMapping(value = "/test2")
public Response<DemoRespDTO> test2(@RequestBody DemoReqDTO reqDTO) {
DemoRespDTO respDTO = new DemoRespDTO().setC(3).setD("4");
return Response.success(respDTO);
}
@SafetyProcess(decode = false)
@PostMapping(value = "/test3")
public Response<DemoRespDTO> test3(@RequestBody DemoReqDTO reqDTO) {
DemoRespDTO respDTO = new DemoRespDTO().setC(3).setD("4");
return Response.success(respDTO);
}
@SafetyProcess(decode = false, encode = false)
@PostMapping(value = "/test4")
public Response<DemoRespDTO> test4(@RequestBody DemoReqDTO reqDTO) {
DemoRespDTO respDTO = new DemoRespDTO().setC(3).setD("4");
return Response.success(respDTO);
}
public static void main(String[] args) throws JsonProcessingException {
DemoReqDTO reqDTO = new DemoReqDTO().setA(1).setB("2");
System.out.println("源字符串:" + new ObjectMapper().writeValueAsString(reqDTO));
String str = encode(reqDTO);
System.out.println("加密后:" + str);
String res = decode(str);
System.out.println("解密后:" + res);
}
private static String encode(Object obj) {
ObjectMapper objectMapper = new ObjectMapper();
try {
String bodyStr = objectMapper.writeValueAsString(obj);
byte[] bytes = SecureUtil.aes("1234567890abcdef".getBytes()).encrypt(bodyStr);
return Base64.getEncoder().encodeToString(bytes);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
private static String decode(String str) {
byte[] bytes = Base64.getDecoder().decode(str);
byte[] body = SecureUtil.aes("1234567890abcdef".getBytes()).decrypt(bytes);
return new String(body, StandardCharsets.UTF_8);
}
}
6.测试效果
curl -X POST \
http://localhost:8080/test \
-H 'Content-Type: application/json' \
-d 'MGi5m7Q0ghkXf+3K/I80qQ=='
{
"code": 200,
"message": "success",
"data": "GHjhCNV5FnFGoO7ITlXjF2lM8lNDX6IX1/wMPNFWF6qIEfenO6LM0G4KP9hbuevFoFjFqKzDDJ8Z0af+TVG1Ww=="
}
- 码云 https://gitee.com/hweiyu/spring-boot-encrypt.git