文章目录
前言
- 出于项目安全方面的考虑,对接口的入参和返回数据进行加解密,综合考虑效率和安全性总重采用AES的对称加密方式(前后台数据传输时采用base64编码否则会出现乱码现象)废话不多说 直接上代码
请求的拦截器类
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
public class AesFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
AesRequestWrapper requestWrapper = null;
if (request instanceof HttpServletRequest) {
requestWrapper = new AesRequestWrapper((HttpServletRequest) request);
}
if (requestWrapper != null ){
chain.doFilter(requestWrapper, response);
}else {
chain.doFilter(request, response);
}
}
@Override
public void destroy() {
}
}
AesRequestWrapper重写读取流的类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.util.Base64;
public class AesRequestWrapper extends HttpServletRequestWrapper {
private static final Logger logger = LoggerFactory.getLogger(AesRequestWrapper.class);
private String body;
public AesRequestWrapper(HttpServletRequest request) {
super(request);
renewBody(request);
}
@Override
public ServletInputStream getInputStream() {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() {
return byteArrayInputStream.read();
}
};
}
@Override
public BufferedReader getReader() {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
private void renewBody(HttpServletRequest request) {
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = request.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
}
body = new String(AesCoder.decrypt(Base64.getDecoder().decode(stringBuilder.toString())));
} catch (Exception ex) {
logger.error("报错请求接口的路径:{}",request.getRequestURI());
logger.error("请求入参aes解密失败:{}",ex);
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException ex) {
logger.error("请求入参base64转换失败:{}",ex);
}
}
}
}
public String getBody() {
return body;
}
}
AesCoder 加解密类
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
public class AesCoder {
private static final String KEY_ALGORITHM = "AES";
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
private static final String PRIVATE_KEY = "wrqiorduis589756";
static Key key;
static {
key = toKey(initSecretKey());
}
private static byte[] initSecretKey() {
return PRIVATE_KEY.getBytes(StandardCharsets.UTF_8);
}
private static Key toKey(byte[] key){
return new SecretKeySpec(key, KEY_ALGORITHM);
}
public static byte[] encrypt(byte[] data) throws Exception{
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
}
public static byte[] decrypt(byte[] data) throws Exception{
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
return cipher.doFinal(data);
}
private static String showByteArray(byte[] data){
if(null == data){
return null;
}
StringBuilder sb = new StringBuilder("{");
for(byte b:data){
sb.append(b).append(",");
}
sb.deleteCharAt(sb.length()-1);
sb.append("}");
return sb.toString();
}
public static void main(String[] args) throws Exception {
String data ="AES数据阿斯器具";
System.out.println("加密前数据: string:"+data);
System.out.println();
byte[] encryptData = encrypt(data.getBytes());
String base64 = Base64.getEncoder().encodeToString(encryptData);
System.out.println("base64 String"+base64);
System.out.println();
byte[] base64Data = Base64.getDecoder().decode(base64);
byte[] decryptData = decrypt(base64Data);
System.out.println("解密后数据: string:"+new String(decryptData, StandardCharsets.UTF_8));
}
}
到此为止接口传参的数据解密就完成了 接下来就是返回数据的加密
DealWithResponseBody 全局数据加密类
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 java.util.Base64;
@ControllerAdvice
public class DealWithResponseBody implements ResponseBodyAdvice {
private static final Logger logger = LoggerFactory.getLogger(DealWithResponseBody.class);
private static final String URL = "/medmanage";
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
String requestPath = request.getURI().getPath();
if(requestPath.startsWith(URL) && !requestPath.contains("upload")){
try {
return Base64.getEncoder().encodeToString(AesCoder.encrypt(JSONObject.toJSONString(body, SerializerFeature.WriteMapNullValue,SerializerFeature.DisableCircularReferenceDetect).getBytes()));
} catch (Exception e) {
logger.error("返回结果的加密失败:{}",e);
}
}
return body;
}
}
最终总结
- spring中的拦截器真的很好用,它体现出来的设计思想就是讲业务代码和逻辑处理分层开,设计模式真的能让人受用终生,它不仅仅体现在编程中,做事学习都有可以借鉴的地方