前后端接口AES+RSA混合加解密详解(vue+SpringBoot)
前后端接口AES+RSA混合加解密
为什么需要对前后端接口加解密?主要是甲方要求。
一、AES加密原理和为什么不使用AES加密
AES是最常见的对称加密算法,加密和解密用到的密钥是相同的,这种加密方式加密速度非常快,适合经常发送数据的场合,且加密长文本比较方便。缺点是密钥的传输比较麻烦,只能将一把公钥分别在前后端代码中各存放一份,还有一个遇到的问题下面会讲到,那就是美国对AES加密密钥长度的限制。优点是加密效率高,缺点是安全性低。
二、RSA加密原理和为什么不使用rsa加密
RSA也就是非对称加密算法,是使用不同密钥进行加密和解密的算法,也称为公私钥加密。公钥和私钥是同时生成的,公钥用来加密,私钥用来解密,加解密的密钥是成对出现。但是不推荐用RSA加密请求报文,因为RSA加密后的报文会很长,经常会出现超过请求体长度限制。优点是安全性高,缺点是RSA的加密效率低。
三、AES和RSA混合加密的原理
我们可以结合两者的优点,AES的加密效率高,那我们可以使用aes来加密报文,而RSA的灵活性和安全性来加密aes的密钥。总的思路就是利用RSA来加密传输AES的密钥,用 AES的密钥来加密请求报文。
四、代码样例
前端
1. 请求增加加密标识
首先这个功能我们的出发点是可以灵活配置,所以我们在需要加密的请求头添加一个加密标识代码如下:
import axios from '@common/plugins/Axios'
saveStudent: data => {
return axios.request({
url: `/demo/saveStudent`,
method: 'post',
headers: {
isEncrypt: 1
},
data
})
}
2. 前端加密工具类
import JSEncrypt from 'jsencrypt'
import CryptoJS from 'crypto-js'
export function rsaEncrypt(Str, afterPublicKey) {
const encryptor = new JSEncrypt()
encryptor.setPublicKey(afterPublicKey)
return encryptor.encrypt(Str)
}
export function rsaDecrypt(Str, frontPrivateKey) {
const encryptor = new JSEncrypt()
encryptor.setPrivateKey(frontPrivateKey)
return encryptor.decrypt(Str)
}
export function aesEncrypt(aeskey, Str) {
var key = CryptoJS.enc.Utf8.parse(aeskey)
var srcs = CryptoJS.enc.Utf8.parse(Str)
var encrypted = CryptoJS.AES.encrypt(srcs, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
})
return encrypted.toString()
}
export function aesDecrypt(aeskey, Str) {
var key = CryptoJS.enc.Utf8.parse(aeskey)
var decrypt = CryptoJS.AES.decrypt(Str, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
})
return CryptoJS.enc.Utf8.stringify(decrypt).toString()
}
export function get16RandomNum() {
var chars = [ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K', 'L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'
]
var nums = ''
for (var i = 0; i < 16; i++) {
var id = parseInt(Math.random() * 61)
nums += chars[id]
}
return nums
}
export function getRsaKeys() {
return new Promise((resolve, reject) => {
window.crypto.subtle
.generateKey(
{
name: 'RSA-OAEP',
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: { name: 'SHA-512' }
},
true,
['encrypt', 'decrypt']
)
.then(function(key) {
window.crypto.subtle
.exportKey('pkcs8', key.privateKey)
.then(function(keydata1) {
window.crypto.subtle
.exportKey('spki', key.publicKey)
.then(function(keydata2) {
var privateKey = RSA2text(keydata1, 1)
var publicKey = RSA2text(keydata2)
resolve({ privateKey, publicKey })
})
.catch(function(err) {
reject(err)
})
})
.catch(function(err) {
reject(err)
})
})
.catch(function(err) {
reject(err)
})
})
}
function RSA2text(buffer, isPrivate = 0) {
var binary = ''
var bytes = new Uint8Array(buffer)
var len = bytes.byteLength
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i])
}
var base64 = window.btoa(binary)
let text = base64.replace(/[^\x00-\xff]/g, '$&\x01').replace(/.{64}\x01?/g, '$&\n')
return text
}
3.前端axios请求统一封装,和返回统一封装
import Axios from 'axios'
import { getRsaKeys, rsaEncrypt, rsaDecrypt, aesDecrypt, aesEncrypt, get32RandomNum } from '@common/util'
const instance = Axios.create({
headers: {
x_requested_with: 'XMLHttpRequest'
}
})
let frontPrivateKey
instance.interceptors.request.use(
async config => {
if (sessionStorage.getItem('X-Access-Token')) {
config.headers['X-Access-Token'] = sessionStorage.getItem('X-Access-Token')
}
if (config.headers['isEncrypt']) {
config.headers['Content-Type'] = 'application/json;charset=utf-8'
if (config.method === 'post' || config.method === 'put') {
const { privateKey, publicKey } = await getRsaKeys()
let afterPublicKey = sessionStorage.getItem('afterPublicKey')
frontPrivateKey = privateKey
let aesKey = get16RandomNum()
let aesKeyByRsa = rsaEncrypt(aesKey, afterPublicKey)
if (config.data) {
let data = aesEncrypt(aesKey, JSON.stringify(config.data))
config.data = {
data: data,
aeskey: aesKeyByRsa,
frontPublicKey: publicKey
}
}
if (config.params) {
let data = aesEncrypt(aesKey, JSON.stringify(config.params))
config.params = {
params: data,
aeskey: aesKeyByRsa,
frontPublicKey: publicKey
}
}
}
}
config.url ="你的后端接口请求地址" + config.url
return config
},
err => {
return Promise.reject(err)
}
)
instance.interceptors.response.use(
response => {
let aesKeyByRsa = response.data.aesKeyByRsa
if (aesKeyByRsa) {
let aesKey = rsaDecrypt(aesKeyByRsa, frontPrivateKey)
response.data.data = JSON.parse(JSON.parse(aesDecrypt(aesKey, response.data.data)))
return response.data
}else{
return response
}
}
},
error => {
if (error.response.status === 500) {
const { data } = error.response
if (data !== null && data !== undefined) {
if (error.response.data.trace.includes('账号已被冻结或注销,请联系管理员!'))
doLogout('账号已被冻结或注销,请联系管理员!')
}
if (error.response.data.trace.includes('用户不存在!')) {
doLogout('用户不存在!')
}
if (error.response.data.trace.includes('Token失效')) {
doLogout('Token失效,请重新登录!')
}
} else {
Notification.error({
title: '提示',
message: error.response.data.error
})
}
} else {
return Promise.reject(error.response)
}
}
)
export default instance
后端
1.后端加密工具类
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Random;
public class AESUtils {
private static final String KEY_ALGORITHM = "AES";
private static final int KEY_LENGTH = 16 * 8;
private static final String ALGORITHMS = "AES/ECB/PKCS5Padding";
public static String key;
static {
key = getKey();
}
public static String getKey() {
int length = KEY_LENGTH / 8;
StringBuilder uid = new StringBuilder(length);
Random rd = new SecureRandom();
for (int i = 0; i < length; i++) {
switch (rd.nextInt(3)) {
case 0:
uid.append(rd.nextInt(10));
break;
case 1:
uid.append((char) (rd.nextInt(26) + 65));
break;
case 2:
uid.append((char) (rd.nextInt(26) + 97));
break;
default:
break;
}
}
return uid.toString();
}
public static String encrypt(String content, String encryptKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), KEY_ALGORITHM));
return Base64.encodeBase64String(cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)));
}
public static String decrypt(String encryptStr, String decryptKey) throws Exception {
byte[] decodeBase64 = Base64.decodeBase64(encryptStr);
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), KEY_ALGORITHM));
return new String(cipher.doFinal(decodeBase64));
}
}
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.LinkedHashMap;
import java.util.Map;
public class RSAUtils {
private static final String KEY_ALGORITHM = "RSA";
private static final String ALGORITHMS = "RSA/ECB/PKCS1Padding";
private static final int MAX_ENCRYPT_BLOCK = 245;
private static final int MAX_DECRYPT_BLOCK = 256;
private static final int INITIALIZE_LENGTH = 2048;
private static final Map<String, String> map = new LinkedHashMap<>(2);
public static Map<String,String> genKeyPair() throws Exception {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(INITIALIZE_LENGTH);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
String publicKeyString = Base64.encodeBase64String(publicKey.getEncoded());
String privateKeyString = Base64.encodeBase64String((privateKey.getEncoded()));
map.put("publicKey",publicKeyString);
map.put("privateKey",privateKeyString);
return map;
}
public static String getPrivateKey(){
return map.get("privateKey");
}
public static String getPublicKey(){
return map.get("publicKey");
}
public static byte[] decryptByPrivateKey(byte[] data, String privateKey) throws Exception {
Key privateK = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.DECRYPT_MODE, privateK);
return encryptAndDecryptOfSubsection(data, cipher, MAX_DECRYPT_BLOCK);
}
public static byte[] encryptByPublicKey(byte[] data, String publicKey) throws Exception {
Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey)));
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.ENCRYPT_MODE, publicK);
return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK);
}
public static byte[] pubKeyDec(byte[] data, String publicKey) throws Exception {
Key privateK = KeyFactory.getInstance(KEY_ALGORITHM).generatePublic(new X509EncodedKeySpec(Base64.decodeBase64(publicKey)));
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.DECRYPT_MODE, privateK);
return encryptAndDecryptOfSubsection(data, cipher, MAX_DECRYPT_BLOCK);
}
public static byte[] privKeyEnc(byte[] data, String privateKey) throws Exception {
Key publicK = KeyFactory.getInstance(KEY_ALGORITHM).generatePrivate(new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
Cipher cipher = Cipher.getInstance(ALGORITHMS);
cipher.init(Cipher.ENCRYPT_MODE, publicK);
return encryptAndDecryptOfSubsection(data, cipher, MAX_ENCRYPT_BLOCK);
}
private static byte[] encryptAndDecryptOfSubsection(byte[] data, Cipher cipher, int encryptBlock) throws Exception {
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
while (inputLen - offSet > 0) {
if (inputLen - offSet > encryptBlock) {
cache = cipher.doFinal(data, offSet, encryptBlock);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * encryptBlock;
}
out.close();
return out.toByteArray();
}
}
import org.apache.commons.codec.binary.Base64;
public class ApiSecurityUtils {
public static String decrypt(String aesKeyByRsa,String decryptStr,String privateKey) throws Exception {
byte[] bytes = RSAUtils.decryptByPrivateKey(Base64.decodeBase64(aesKeyByRsa), privateKey);
String aesKey = new String(bytes);
return AESUtils.decrypt(decryptStr, aesKey);
}
public static ApiEncryptRes encrypt(String encryptStr, String frontPublicKey) throws Exception {
String aesKey = AESUtils.getKey();
String data = AESUtils.encrypt(encryptStr, aesKey);
ApiEncryptRes apiEncryptRes = new ApiEncryptRes();
String aesKeyByRsa = Base64.encodeBase64String(RSAUtils.encryptByPublicKey(aesKey.getBytes(), frontPublicKey));
apiEncryptRes.setAesKeyByRsa(aesKeyByRsa);
apiEncryptRes.setData(data);
return apiEncryptRes;
}
}
import java.security.MessageDigest;
public class MD5Util {
private final static String[] hexDigits = {"0", "1", "2", "3", "4",
"5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
public static String md5(String inputString) {
return encodeByMD5(inputString);
}
private static String encodeByMD5(String originString) {
if (originString != null) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] results = md.digest(originString.getBytes("utf-8"));
String resultString = byteArrayToHexString(results);
return resultString.toUpperCase();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return null;
}
private static String byteArrayToHexString(byte[] b) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++) {
resultSb.append(byteToHexString(b[i]));
}
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n += 256;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
}
2. 请求的返回实体封装
import lombok.Data;
@Data
public class ApiEncryptRes {
private String aesKeyByRsa;
private String data;
private String frontPublicKey;
}
3. 配置过滤器对请求进行加解密操作
import cn.hutool.core.convert.Convert;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.l.common.encryptAPI.utils.ApiSecurityUtils;
import com.l.common.encryptAPI.util.MD5Util;
import com.l.common.encryptAPI.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.http.ContentType;
import lombok.SneakyThrows;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.util.WebUtils;
import java.util.TreeMap;
@Slf4j
public class EncryptRequestWrapper extends HttpServletRequestWrapper {
protected FastByteArrayOutputStream cachedContent;
protected String JSPublicKey;
protected EncryptRequestWrapper(HttpServletRequest request ) {
super(request);
RedisUtil redisUtils =getBean(RedisUtil.class, request);
this.copyBody(redisUtils);
}
public <T> T getBean(Class<T> clazz, HttpServletRequest request){
WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
return applicationContext.getBean(clazz);
}
public static EncryptRequestWrapper newOrGetMultiReadHttpServletRequest(HttpServletRequest request) {
EncryptRequestWrapper multiReadHttpServletRequest = getMultiReadHttpServletRequest(request);
if (null != multiReadHttpServletRequest) {
return multiReadHttpServletRequest;
} else {
String contentType = request.getContentType();
if (contentType != null && contentType.contains(ContentType.MULTIPART.getValue())) {
request = new StandardServletMultipartResolver().resolveMultipart(request);
}
return new EncryptRequestWrapper(request);
}
}
public static EncryptRequestWrapper getMultiReadHttpServletRequest(HttpServletRequest request) {
EncryptRequestWrapper nativeRequest = WebUtils.getNativeRequest(request, EncryptRequestWrapper.class);
if (null != nativeRequest) {
return nativeRequest;
} else if (request instanceof EncryptRequestWrapper) {
return (EncryptRequestWrapper) request;
} else {
return null;
}
}
@SneakyThrows
protected void copyBody(RedisUtil redisUtils) {
int length = this.getContentLength();
if (length > 0) {
cachedContent = IoUtil.read(getRequest().getInputStream());
if (StringUtils.isNotBlank(this.getHeader("Isencrypt"))&&StringUtils.isNotBlank(this.getHeader("X-Access-Token"))) {
String body = new String(cachedContent.toByteArray());
log.info("------------------- body = " + body + "------------------------");
if (StringUtils.isNotBlank(body)) {
JSONObject jsonBody = JSONObject.parseObject(body);
if(null != jsonBody){
String dataEncrypt = jsonBody.getString("data");
String aeskey = jsonBody.getString("aeskey");
JSPublicKey = jsonBody.getString("frontPublicKey");
String data;
JSONObject json = null;
log.info("------------------- dataEncrypt = " + dataEncrypt + "------------------------");
log.info("------------------- aesKey = " + aeskey + "------------------------");
String token = this.getHeader("X-Access-Token");
String md5Token = MD5Util.md5(token);
String privateKey = Convert.toStr(redisUtils.get(md5Token + "privateKey"));
data = ApiSecurityUtils.decrypt(aeskey, dataEncrypt,privateKey);
if (StringUtils.isNotBlank(data)) {
if ("undefined".equalsIgnoreCase(data)) {
json = new JSONObject();
} else {
json = JSONObject.parseObject(data);
}
}
if (json != null) {
body = json.toJSONString();
}
}
log.info("------------------- body = " + body + "------------------------");
cachedContent.reset();
cachedContent.write( body.getBytes(), 0, body.getBytes().length);
}
}
} else {
cachedContent = new FastByteArrayOutputStream();
}
}
@Override
public int getContentLength() {
if (null != cachedContent) {
return cachedContent.size();
}
return super.getContentLength();
}
public byte[] getByteArrayBody() {
return cachedContent.toByteArray();
}
public String getBody() {
String body = null;
if (cachedContent != null && cachedContent.size() > 0) {
body = new String(cachedContent.toByteArray());
}
return body;
}
public String getSortBody() {
return JSONObject.toJSONString(this.getBody(TreeMap.class));
}
public <T> T getBody(Class<T> type) {
return cn.hutool.json.JSONUtil.toBean(this.getBody(), type);
}
public String getJSPublicKey(){
return JSPublicKey;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (this.getContentLength() < 1) {
return super.getInputStream();
}
return new ResettableServletInputStream(new ByteArrayInputStream(cachedContent.toByteArray()));
}
private class ResettableServletInputStream extends ServletInputStream {
private final InputStream sourceStream;
private boolean finished = false;
public ResettableServletInputStream(InputStream sourceStream) {
Assert.notNull(sourceStream, "Source InputStream must not be null");
this.sourceStream = sourceStream;
}
public final InputStream getSourceStream() {
return this.sourceStream;
}
@Override
public int read() throws IOException {
int data = this.sourceStream.read();
if (data == -1) {
this.finished = true;
}
return data;
}
@Override
public int available() throws IOException {
return this.sourceStream.available();
}
@Override
public void close() throws IOException {
super.close();
this.sourceStream.close();
}
@Override
public boolean isFinished() {
return finished;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
}
}
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@Slf4j
public class ResponseWrapper extends HttpServletResponseWrapper {
ByteArrayOutputStream _stream = new ByteArrayOutputStream();
PrintWriter _pw = new PrintWriter(_stream);
public ResponseWrapper(HttpServletResponse resp) throws IOException {
super(resp);
}
@Override
public PrintWriter getWriter() throws IOException {
return _pw;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new ServletOutputStream() {
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener writeListener) {
}
@Override
public void write(int b) throws IOException {
_stream.write(b);
}
};
}
public void flush() {
try {
_pw.flush();
_pw.close();
_stream.flush();
_stream.close();
} catch (IOException e) {
log.error("", e);
}
}
public ByteArrayOutputStream getByteArrayOutputStream() {
return _stream;
}
public String getTextContent() {
flush();
return _stream.toString();
}
public static boolean isTextContentType(String contentType) {
boolean flag = false;
if (StrUtil.isBlank(contentType)) {
return false;
} else {
flag = StrUtil.startWithIgnoreCase(contentType, "text");
if (!flag) {
flag = isJsonContentType(contentType);
}
}
return flag;
}
public static boolean isJsonContentType(String contentType) {
return !StrUtil.isBlank(contentType)
&& (StrUtil.containsIgnoreCase(contentType, "application/problem+json")
|| StrUtil.containsIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE));
}
}
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.l.common.encryptAPI.pojo.ApiEncryptRes;
import com.l.common.encryptAPI.utils.ApiSecurityUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.web.servlet.filter.OrderedFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
@Slf4j
@Component
public class EncryptApiFilter extends OncePerRequestFilter implements OrderedFilter {
public static final int DEFAULT_ORDER = Integer.MAX_VALUE;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String servletPath = request.getServletPath();
ResponseWrapper wrapper = new ResponseWrapper(response);
String requestContent = null;
if (ServletUtil.isMultipart(request)) {
} else {
MultiReadHttpServletRequestWrapper multiReadHttpServletRequestWrapper = MultiReadHttpServletRequestWrapper.newOrGetMultiReadHttpServletRequest(request);
requestContent = multiReadHttpServletRequestWrapper.getBody();
request = multiReadHttpServletRequestWrapper;
}
log.debug("{}>>>{}", servletPath, requestContent);
try {
filterChain.doFilter(request, wrapper);
} finally {
try {
process(request, wrapper, response);
} catch (Exception e) {
log.error("AesFilter error", e);
}
}
}
@SneakyThrows
protected void process(HttpServletRequest request, ResponseWrapper responseWrapper, HttpServletResponse response) {
String servletPath = request.getRequestURI();
String isencrypt = request.getHeader("Isencrypt");
ServletOutputStream out = response.getOutputStream();
if (ResponseWrapper.isJsonContentType(responseWrapper.getContentType())) {
String responseContent = responseWrapper.getTextContent();
if (StrUtil.isNotEmpty(responseContent)&&StringUtils.isNotBlank(isencrypt)) {
String JSPublicKey = ((MultiReadHttpServletRequestWrapper)request).getJSPublicKey();
if (StringUtils.isNotBlank(JSPublicKey)) {
ApiEncryptRes apiEncryptRes = ApiSecurityUtils.encrypt(JSON.toJSONString(responseContent), JSPublicKey);
responseContent = JSONUtil.toJsonStr(apiEncryptRes);
}
log.info("{}<<<{}", servletPath, responseContent);
byte[] bytes = responseContent.getBytes(StandardCharsets.UTF_8);
response.setContentLength(bytes.length);
out.write(bytes);
out.flush();
out.close();
} else {
responseWrapper.getByteArrayOutputStream().writeTo(out);
out.flush();
out.close();
}
} else {
responseWrapper.getByteArrayOutputStream().writeTo(out);
out.flush();
out.close();
}
}
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
}
@Override
public int getOrder() {
return DEFAULT_ORDER;
}
}
4.过滤器注册
import com.l.common.encryptAPI.filter.EncryptApiFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class EncryConfig {
@Bean
@ConditionalOnWebApplication
public EncryptApiFilter encryptApiFilter(ApplicationContext applicationContext) {
return new EncryptApiFilter();
}
}
5. 获取RSA公钥接口
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.extension.api.R;
import com.l.common.encryptAPI.utils.RSAUtils;
import com.l.common.encryptAPI.util.MD5Util;
import com.l.common.encryptAPI.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
@RestController
@RequestMapping("/encrypt")
@Slf4j
public class EncryptController {
@Autowired
private RedisUtil redisUtil;
@GetMapping("/getPublicKey")
public R<?> getPublicKey(HttpServletRequest request) throws Exception {
String publicKey="";
if (StringUtils.isNotBlank(token)) {
Map<String, String> stringStringMap = RSAUtils.genKeyPair();
publicKey = stringStringMap.get("publicKey");
String privateKey = stringStringMap.get("privateKey");
String md5Token = MD5Util.md5(token);
redisUtil.set(md5Token + "publicKey", publicKey);
redisUtil.set(md5Token + "privateKey", privateKey);
return R.ok(publicKey);
}
return R.ok(publicKey);
}
}
总结: