概述
- 使用对称加密的方式实现。
- 前端基于crypto-js。
- uni-app框架中是在uni.request的基础上,在拦截器中处理的。
- springboot在Filter中完成解密工作。
uni-app
- 项目中引入crypto-js。
npm install crypto-js
- 加密方法
const SECRET_KEY = CryptoJS.enc.Utf8.parse("1234123412341234");
function encrypt (msg) {
const dataHex = CryptoJS.enc.Utf8.parse(msg);
const encrypted = CryptoJS.AES.encrypt(dataHex, SECRET_KEY, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
}
- 解密方法
function decrypt(msg) {
const encryptedHexStr = CryptoJS.enc.Hex.parse(msg);
const str = CryptoJS.enc.Base64.stringify(encryptedHexStr);
const decrypt = CryptoJS.AES.decrypt(str, SECRET_KEY, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return decrypt.toString(CryptoJS.enc.Utf8);
}
- request拦截器
uni.addInterceptor('request', {
invoke(args) {
let plaintext = JSON.stringify(args.data);
plaintext = encodeURIComponent(plaintext);
const textArray = [];
while(plaintext.length > 15) {
textArray.push(plaintext.substring(0, 16));
plaintext = plaintext.substring(16);
}
textArray.push(plaintext);
const encryptParamArray = [];
textArray.forEach(item => {
encryptParamArray.push(btoa(encrypt(item)));
})
args.data = {"encryptParams": encryptParamArray};
},
success(args) {
},
fail(err) {
},
complete(res) {
}
});
备注
- 使用encodeURIComponent方法是为了处理 字符“+”,这个对应java解密的时候存在问题。
- 该模式默认解密长度出限制在16个字符中,所以需要将待加密的信息分解成单个字符长度小于16的字符组成数组。
Springboot
- DecryptFilter,解密拦截器
import cn.hutool.json.JSONUtil;
import org.apache.commons.codec.binary.Base64;
import org.springframework.http.HttpMethod;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
@WebFilter(urlPatterns = "/*")
public class DecryptFilter implements Filter {
private String word;
public DecryptFilter(String word) {
this.word = word;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
if (HttpMethod.OPTIONS.matches(httpRequest.getMethod())) {
filterChain.doFilter(httpRequest, servletResponse);
return;
}
String encryptedData = "";
if (httpRequest.getHeader("Content-Type").contains("x-www-form-urlencoded")) {
encryptedData = httpRequest.getParameter("encryptParams");
} else if (httpRequest.getHeader("Content-Type").contains("json")) {
StringBuilder stringBuilder = new StringBuilder();
try (InputStream inputStream = httpRequest.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line);
}
}
encryptedData = JSONUtil.parseObj(stringBuilder.toString()).get("encryptParams").toString();
encryptedData = encryptedData.replaceAll("[\\[\\]\"]", "");
}
String[] ciphertextArray = encryptedData.split(",");
String decryptedData = "";
try {
decryptedData = decrypt(ciphertextArray);
} catch (Exception e) {
throw new RuntimeException("解密失败!", e);
}
ServletRequest modifiedRequest = new BodyRewritingRequestWrapper(httpRequest, decryptedData);
filterChain.doFilter(modifiedRequest, servletResponse);
}
@Override
public void destroy() {
}
private String decrypt(String[] encryptedTextArray) throws Exception {
StringBuilder paramsJson = new StringBuilder("");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
byte[] keyBytes = word.getBytes(StandardCharsets.UTF_8);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
for (String ciphertext : encryptedTextArray) {
byte[] decode = java.util.Base64.getDecoder().decode(ciphertext);
byte[] encryptedBytes = Base64.decodeBase64(decode);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
paramsJson.append(new String(decryptedBytes, StandardCharsets.UTF_8));
}
return URLDecoder.decode(paramsJson.toString(), "UTF-8");
}
}
- BodyRewritingRequestWrapper,重写的ServletRequest对相关
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
public class BodyRewritingRequestWrapper extends HttpServletRequestWrapper {
private String body;
private JSONObject map;
public BodyRewritingRequestWrapper(HttpServletRequest request, String body) {
super(request);
this.body = body;
this.map = JSONUtil.parseObj(body);
}
@Override
public String getParameter(String name) {
if (map.containsKey(name)) {
return map.get(name).toString();
}
return super.getParameter(name);
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> originalParameters = super.getParameterMap();
Map<String, String[]> rewriteMap = new HashMap<>(originalParameters);
map.forEach((key, value) -> rewriteMap.put(key, new String[]{value.toString()}));
return rewriteMap;
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
return new ServletInputStream() {
public int read() throws IOException {
return byteArrayInputStream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
- 注册拦截器
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class WebConfig {
@Value("${decrypt.word}")
private String word;
@Bean
public FilterRegistrationBean<DecryptFilter> myFilterRegistration() {
FilterRegistrationBean<DecryptFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new DecryptFilter(word));
registration.addUrlPatterns("/*");
registration.setName("decryptFilter");
registration.setOrder(1);
return registration;
}
}