uni-app和springboot完成前端后端对称加密解密流程

概述

  • 使用对称加密的方式实现。
  • 前端基于crypto-js。
  • uni-app框架中是在uni.request的基础上,在拦截器中处理的。
  • springboot在Filter中完成解密工作。

uni-app

  1. 项目中引入crypto-js。
npm install crypto-js
  1. 加密方法
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);
}
  1. 解密方法
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);
}
  1. 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

  1. 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(",");

        // 解密操作,例如使用AES解密
        String decryptedData = "";
        try {
            decryptedData = decrypt(ciphertextArray);
        } catch (Exception e) {
            throw new RuntimeException("解密失败!", e);
        }

        // 重构ServletRequest,将解密后的数据设置到新的ServletRequest中
        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");
    }
}
  1. 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()));
    }
}
  1. 注册拦截器
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;
    }
}

你可能感兴趣的:(Java,Spring,uni-app,spring,boot)