概述
- 使用对称加密的方式实现。
- 前端基于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;}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic 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);}@Overridepublic 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);}@Overridepublic String getParameter(String name) {if (map.containsKey(name)) {return map.get(name).toString();}return super.getParameter(name);}@Overridepublic 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;}@Overridepublic ServletInputStream getInputStream() throws IOException {final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());return new ServletInputStream() {public int read() throws IOException {return byteArrayInputStream.read();}@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return true;}@Overridepublic void setReadListener(ReadListener readListener) {}};}@Overridepublic 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;@Beanpublic FilterRegistrationBean<DecryptFilter> myFilterRegistration() {FilterRegistrationBean<DecryptFilter> registration = new FilterRegistrationBean<>();registration.setFilter(new DecryptFilter(word));registration.addUrlPatterns("/*");registration.setName("decryptFilter");registration.setOrder(1); return registration;}
}