java中实现接口统一加解密
可以直接落地的java统一接口加解密方案
·
项目中要实现接口的统一加解密, 网上看了一圈.感觉都不太合适, 也踩了很多的坑;
有的说用拦截器. 有的说过滤器.还有的说用aop, 当然条条大路通罗马,用什么技术都能实现, 我选了一个看上去更清晰的方案. 用@ControllerAdvice, 当然已经有相关的技术方案,但是没有具体代码, 没说怎么处理request和response, 所有我结合其他文章.整理出这一篇直接可以落地的;
思路
- 使用 @ControllerAdvice + RequestBodyAdviceAdapter 处理request进行解密;
- 使用 @ControllerAdvice + ResponseBodyAdvice 对response进行加密;
代码
- 前置
这里直接用springboot演示的.默认你会用
里面用到了Lombok插件; 省略了get,set方法; 你要是不用也可以, 自己生成下get,set就行;
使用了hutool的工具类;
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.1</version>
</dependency>
实体类
package com.example.boottest.domain;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
*
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SysTestUser implements Serializable {
/**
*
*/
private Long id;
/**
*
*/
private String name;
/**
*
*/
private Integer age;
}
- 处理request请求 如下:
package com.example.boottest.filter;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.json.JSONUtil;
import com.example.boottest.domain.SysTestUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
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.RequestBodyAdviceAdapter;
import javax.crypto.SecretKey;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
@Slf4j
@ControllerAdvice
public class DecryptRequestBodyAdvice extends RequestBodyAdviceAdapter {
public static final String KEY = "e75e2d3126357d031ddaf412f9110f66";
public static void main(String[] args) {
SysTestUser sysTestUser = new SysTestUser();
sysTestUser.setAge(12);
sysTestUser.setId(1L);
sysTestUser.setName("lily");
encrypt(JSONUtil.toJsonStr(sysTestUser));
}
/**
* 生成key 用于演示加解密. 这个应该是前后端约定好的;
*
*/
private static void getKey() {
SecretKey aes1 = KeyUtil.generateKey("AES");
byte[] encoded = aes1.getEncoded();
String str = HexUtil.encodeHexStr(encoded);
System.out.println(str);//就是KEY
}
/**
* 加密
*
*/
private static void encrypt(String str) {
//解密
AES aes = new AES(HexUtil.decodeHex(KEY));
String encryptHex = aes.encryptHex(str.getBytes(StandardCharsets.UTF_8));
System.out.println(encryptHex);
}
/**
* 解密
*
*/
private static void decrypt(String str) {
//解密
AES aes = new AES(HexUtil.decodeHex(KEY));
String decryptStr = aes.decryptStr(str);
System.out.println(decryptStr);
}
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<?
extends HttpMessageConverter<?>> converterType) throws IOException {
HttpHeaders headers = inputMessage.getHeaders();
//这里的流只能读一次. 后面无法读取了
byte[] bytes = IoUtil.readBytes(inputMessage.getBody(),false);
//解密
AES aes = new AES(HexUtil.decodeHex(KEY));
byte[] decrypt = aes.decrypt( HexUtil.decodeHex(new String(bytes)));
//将解密之后的body数据重新封装为HttpInputMessage作为当前方法的返回值
InputStream inputStream = IoUtil.toStream(decrypt);
return new HttpInputMessage() {
@Override
public InputStream getBody() throws IOException {
return inputStream;
}
@Override
public HttpHeaders getHeaders() {
return inputMessage.getHeaders();
}
};
}
}
上述的重点是:
InputStream inputStream = IoUtil.toStream(decrypt);
– 这一步是把解密后的data转化为新的流
return new HttpInputMessage() {}
– 重写HttpInputMessage进行返回
- 处理response 如下:
package com.example.boottest.filter;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
@Slf4j
@ControllerAdvice
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {
public static final String KEY = "e75e2d3126357d031ddaf412f9110f66";
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//获取response, 你可能用得到
ServletServerHttpResponse servletServerHttpResponse =(ServletServerHttpResponse) response;
HttpServletResponse servletResponse = servletServerHttpResponse.getServletResponse();
//获取request, 你可能用得到
ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) request;
HttpServletRequest servletRequest = servletServerHttpRequest.getServletRequest();
String jsonStr = JSONUtil.toJsonStr(body);
log.info("原本的返回值:[{}]",jsonStr);
//对返回加密;
AES aes = new AES(HexUtil.decodeHex(KEY));
byte[] decrypt = aes.encrypt(jsonStr.getBytes(StandardCharsets.UTF_8));
log.info("加密后的hex:[{}]",HexUtil.encodeHexStr(decrypt));
return HexUtil.encodeHexStr(decrypt);
}
}
代码里面的加解密方式我用的是aes进行演示的;你自己可以替换成rsa或者其他;
自己生成了key
/**
* 生成key 用于演示加解密. 这个应该是前后端约定好的;
*
*/
private static void getKey() {
SecretKey aes1 = KeyUtil.generateKey("AES");
byte[] encoded = aes1.getEncoded();
String str = HexUtil.encodeHexStr(encoded);
System.out.println(str);//就是KEY
}
public static final String KEY = “e75e2d3126357d031ddaf412f9110f66”;
- 准备controller
package com.example.boottest.controller;
import cn.hutool.json.JSONUtil;
import com.example.boottest.domain.SysTestUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletRequest;
import java.io.IOException;
@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
/**
* test1
*
* @return
*/
@GetMapping("/path")
public SysTestUser test1(@RequestBody SysTestUser user , ServletRequest servletRequest) throws IOException {
log.info("user:[{}]", JSONUtil.toJsonStr(user));
return user;
}
}
- 生成前端请求数据
public static void main(String[] args) {
SysTestUser sysTestUser = new SysTestUser();
sysTestUser.setAge(12);
sysTestUser.setId(1L);
sysTestUser.setName("lily");
encrypt(JSONUtil.toJsonStr(sysTestUser));
}
/**
* 加密
*
*/
private static void encrypt(String str) {
//解密
AES aes = new AES(HexUtil.decodeHex(KEY));
String encryptHex = aes.encryptHex(str.getBytes(StandardCharsets.UTF_8));
System.out.println(encryptHex);
}
生成的数据为:ab04c5d07329c9dfe685167634af14bef36810876f84660494ec7d67c3feda71
测试与结果
- postman测试
- 后端打印结果
可以看到:
postman请求时是加密数据–>在DecryptRequestBodyAdvice中被解析成明文.–>传递到controller中被打印出来user对象,–>在EncryptResponseBodyAdvice中再把对象进行加密后返回给postman;
最后
终于完成了接口统一加解密;
当然如果你想拦截特定的请求,也可以在header里面添加特定的值, 进行判断;或者其他方式;
如果帮助到了你. 欢迎点赞收藏,谢谢;
更多推荐
已为社区贡献1条内容
所有评论(0)