package com.zhxk.ruralbus.config;
import com.zhxk.ruralbus.utils.aes.ParamResultFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author ranzhang
*/
@Configuration
public class AesFilterConfig {
@Bean
public FilterRegistrationBean registerParamFilter(ParamResultFilter paramResultFilter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(paramResultFilter);
registrationBean.setName("paramResultFilter");
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(2);
return registrationBean;
}
}
package com.zhxk.ruralbus.utils.aes;
import java.nio.charset.StandardCharsets;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.zhxk.ruralbus.exception.BusinessException;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
/**
* Description AES CBC + BASE64加密
*
* @author
* @date 14:58 2022/3/31
*/
@Component
public final class AesEncryptUtil {
private AesEncryptUtil() {}
/**
* 加密算法
* @param content 内容
* @param sKey 密钥
* @param ivParameter 偏移量
* @param padding 加密方式
* @return 加密结果
*/
public static String encrypt(String content, String sKey, String ivParameter,String padding) {
try {
Cipher cipher = Cipher.getInstance(padding);
byte[] raw = sKey.getBytes();
SecretKeySpec skySpec = new SecretKeySpec(raw, "AES");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, skySpec, iv);
byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
return Base64Utils.encodeToString(encrypted);
} catch (Exception e) {
throw new BusinessException("加密失败:" + e.getMessage());
}
}
/**
* 解密算法
* @param content 内容
* @param sKey 密钥
* @param ivParameter 偏移量
* @param padding 加密方式
* @return 解密结果
*/
public static String desEncrypt(String content, String sKey, String ivParameter,String padding) {
try {
byte[] raw = sKey.getBytes(StandardCharsets.US_ASCII);
SecretKeySpec skySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance(padding);
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skySpec, iv);
byte[] encrypted1 = Base64Utils.decodeFromString(content);
byte[] original = cipher.doFinal(encrypted1);
return new String(original, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new BusinessException("解密失败:" + e.getMessage());
}
}
/**
* 将二进制转换成16进制
*
* @param buf 参数
* @return 返回值
*/
public static String parseByte2HexStr(byte[] buf) {
StringBuilder sb = new StringBuilder();
for (byte b : buf) {
String hex = Integer.toHexString(b & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
/**
* 将16进制转换为二进制
*
* @param hexStr 参数
* @return 返回值
*/
public static byte[] parseHexStr2Byte(String hexStr) {
if (hexStr.length() < 1) {
return new byte[0];
}
byte[] result = new byte[hexStr.length() / 2];
for (int i = 0; i < hexStr.length() / 2; i++) {
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
result[i] = (byte)(high * 16 + low);
}
return result;
}
}
package com.zhxk.ruralbus.utils.aes;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fastjson.JSONArray;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
/***
* Description 请求参数加密过滤器
*
* @date 16:34 2022/3/29
*/
@Component
@Slf4j
public class ParamResultFilter implements Filter {
/**
* 参数是否加密
*/
@Value("${aesencrypt.paramEncryptAble:false}")
private Boolean paramEncryptAble;
/**
* 结果是否加密
*/
@Value("${aesencrypt.resultEncryptAble:false}")
private Boolean resultEncryptAble;
/**
* 排除加密解密的接口
*/
@Value("${aesencrypt.excludeUrl:}")
private String[] excludeUrls;
/**
* 使用AES-128-CBC加密模式,key需要为16位,key和iv可以相同!
*/
@Value("${aesencrypt.key:}")
private String key;
@Value("${aesencrypt.iv:}")
private String iv;
@Value("${aesencrypt.padding:}")
private String padding;
private List<String> excludes;
/**
* GET 请求方式
*/
private static final String GET_REQUEST = "GET";
/**
* GET 请求方式
*/
private static final String OPTIONS = "OPTIONS";
/**
* DELETE 请求方式
*/
private static final String DELETE_REQUEST = "DELETE";
private static final String POST_REQUEST = "POST";
/**
* 前端加密的请求,swagger 传输的时候没有这个请求头
*/
private static final String ADVANCED = "Advanced";
@SneakyThrows
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
HttpServletRequest req = (HttpServletRequest)request;
String method = req.getMethod();
String requestUri = req.getRequestURI();
// 这里处理swagger 的过滤
boolean swaggerFlag = !StringUtils.isEmpty(requestUri) && requestUri.contains("/doc.html")
|| requestUri.contains("/wn-web/doc.html") || requestUri.contains("/swagger")
|| requestUri.contains("/v2/") ||requestUri.contains("/websocket");
if (DELETE_REQUEST.equals(method) || OPTIONS.equals(method) || swaggerFlag) {
// 这里是过滤器注册转发
chain.doFilter(request, response);
return;
}
// 允许跳过的接口
if (handleExcludeUrl(req)) {
// 这里是过滤器注册转发
chain.doFilter(request, response);
return;
}
// 是否参数加密
RequestWrapper requestWrapper = new RequestWrapper((HttpServletRequest)request);
// 前端加密之后的请求头
String product = req.getHeader("Product");
// 处理智搜的GET加密请求
if (ADVANCED.equals(product) && GET_REQUEST.equals(method) && paramEncryptAble) {
this.dealGetRequestDecode(requestWrapper);
}
// 如果可以json化一般都是swagger请求就直接请求
boolean jsonFlag = false;
// 处理post请求
if (POST_REQUEST.equals(method)) {
StringBuilder str = new StringBuilder();
String line;
BufferedReader reader;
reader = requestWrapper.getReader();
while ((line = reader.readLine()) != null) {
str.append(line);
}
String json;
if (StringUtils.isNotEmpty(str.toString())) {
try {
JSONObject object = JSON.parseObject(str.toString());
json = object.toJSONString();
requestWrapper.setBody(json.getBytes(StandardCharsets.UTF_8));
// 能json 话说明是swagger的
jsonFlag = true;
} catch (Exception e) {
try {
JSONArray object = JSON.parseArray(str.toString());
json = object.toJSONString();
requestWrapper.setBody(json.getBytes(StandardCharsets.UTF_8));
// 能json 话说明是swagger的
jsonFlag = true;
} catch (Exception exception) {
log.warn("不是常规的json字符串解密异常字符串:{},异常内容:{}", str, e.getMessage());
}
}
}
// 如果不是swagger传过来的数据否则就对post进行解密且对返回的数据加密
if (!jsonFlag) {
// post请求内容需要解密
json = AesEncryptUtil.desEncrypt(str.toString(), key, iv, padding);
log.info("解密之后的字符串:{}", json);
requestWrapper.setBody(json.getBytes(StandardCharsets.UTF_8));
}
}
// 结果是否加密
if (resultEncryptAble && !jsonFlag) {
// 且对返回数据进行加密 ,此地方代码过于冗余后续可能需要对整个方法进行优化
this.encryptResultData(chain, response, requestWrapper);
} else {
chain.doFilter(requestWrapper, response);
}
}
/**
* @description: 加密返回数据
* @param: [chain,
* response, requestWrapper]
* @return: void
* @date: 2022/4/12 18:16
*/
public void encryptResultData(FilterChain chain, ServletResponse response, RequestWrapper requestWrapper) {
// 且对返回数据进行加密 ,此地方代码过于冗余后续可能需要对整个方法进行优化
try {
// 请注意响应处理 包装响应对象 res 并缓存响应数据,只有需要加密数据才使用这个 ResponseWrapper
ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse)response);
// 执行业务逻辑 交给下一个过滤器或servlet处理
// 设置响应内容格式,防止解析响应内容时出错
// responseWrapper.setContentType("text/plain;charset=UTF-8");
// 这里是过滤器注册转发
chain.doFilter(requestWrapper, responseWrapper);
// 是否结果加密
byte[] resData = responseWrapper.getResponseData();
// 加密响应报文并响应
String resultEncrypt = AesEncryptUtil.encrypt(new String(resData), key, iv, padding);
// 这里得用原始的流去处理返回 切记
PrintWriter out = response.getWriter();
out.print(resultEncrypt);
out.flush();
out.close();
} catch (Exception e) {
log.error("返回数据加密失败:{}", e.getMessage());
try {
getFailResponse((HttpServletResponse)response);
} catch (IOException ioException) {
log.error("io异常:{}", ioException.getMessage());
}
}
}
/**
* 前端get请求 加密之后需要解码请求参数
*
*/
private void dealGetRequestDecode(RequestWrapper requestWrapper) {
Map<String, String[]> parameterMap = requestWrapper.getParameterMap();
Map<String, Object> paras = new HashMap<>();
// 这里get 请求加密的value
parameterMap.forEach((paramKey, encryptValue) -> {
for (String s : encryptValue) {
try {
// 解密请求的
String params = AesEncryptUtil.desEncrypt(s, key, iv, padding);
JSONObject jsonObject = JSON.parseObject(params);
jsonObject.forEach((name, value) -> {
String[] vs = {value + ""};
paras.put(name, vs);
});
} catch (Exception e) {
log.info("get请求解密异常:{}", e.getMessage());
}
}
});
requestWrapper.addAllParameters(paras);
log.info("paramsMap:{}", parameterMap);
}
private void getFailResponse(HttpServletResponse response) throws IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null;
out = response.getWriter();
// out.write("{\n" +
// " \"status\":"+ Constant.ENCRYPT_FAIL +",\n" +
// " \"message\": null,\n" +
// " \"data\": []\n" +
// "}");
// 加密后的错误消息
out.write("服务端加密异常");
out.flush();
out.close();
}
@Override
public void destroy() {}
@Override
public void init(FilterConfig filterConfig) {
excludes = Lists.newArrayList(Arrays.asList(excludeUrls));
/*String excludesTemp = filterConfig.getInitParameter("excludes");
if (!StrUtil.isEmpty(excludesTemp)) {
String[] url = excludesTemp.split(",");
excludes.addAll(Arrays.asList(url));
}*/
}
private boolean handleExcludeUrl(HttpServletRequest request) {
if (CollectionUtils.isEmpty(excludes)) {
return false;
}
String url = request.getServletPath();
for (String pattern : excludes) {
Pattern p = Pattern.compile("^" + pattern);
Matcher m = p.matcher(url);
if (m.find()) {
return true;
}
}
return false;
}
}
package com.zhxk.ruralbus.utils.aes;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
/**
* @version 1.0
* @date 2021/12/16 14:42
*/
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {
private Map<String, String[]> params = new HashMap<>();
private byte[] body;
public RequestWrapper(HttpServletRequest request) {
super(request);
body = getBodyString(request).getBytes(StandardCharsets.UTF_8);
//将参数表,赋予给当前的Map以便于持有request中的参数
this.params.putAll(request.getParameterMap());
}
/**
* 在获取所有的参数名,必须重写此方法,否则对象中参数值映射不上
*
* @return
*/
@Override
public Enumeration<String> getParameterNames() {
return new Vector<>(params.keySet()).elements();
}
/**
* 重写getParameter方法
*
* @param name 参数名
* @return 返回参数值
*/
@Override
public String getParameter(String name) {
String[] values = params.get(name);
if (values == null || values.length == 0) {
return null;
}
return values[0];
}
@Override
public String[] getParameterValues(String name) {
String[] values = params.get(name);
if (values == null || values.length == 0) {
return new String[]{};
}
return values;
}
/**
* 增加多个参数
*
* @param otherParams 增加的多个参数
*/
public void addAllParameters(Map<String, Object> otherParams) {
for (Map.Entry<String, Object> entry : otherParams.entrySet()) {
addParameter(entry.getKey(), entry.getValue());
}
}
/**
* 增加参数
*
* @param name 参数名
* @param value 参数值
*/
public void addParameter(String name, Object value) {
if (value != null) {
if (value instanceof String[]) {
params.put(name, (String[]) value);
} else if (value instanceof String) {
params.put(name, new String[]{(String) value});
} else {
params.put(name, new String[]{String.valueOf(value)});
}
}
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
/**
* 重写获取 输入流的方法,保证流可写可读多次
*
* @return ServletInputStream
* @throws IOException IO异常
*/
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bs = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
log.info(readListener.toString());
}
@Override
public int read() throws IOException {
return bs.read();
}
};
}
public byte[] getBody() {
return body;
}
public void setBody(byte[] body) {
this.body = body;
}
public static String getBodyString(ServletRequest request) {
StringBuilder sb = new StringBuilder();
try (
InputStream inputStream = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
log.error("getBodyString IOException", e);
}
return sb.toString();
}
}
package com.zhxk.ruralbus.utils.aes;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
/**
* @ClassName: ResponseWrapper
* @Description:
* @Date: 2022-04-11 10:41
*/
@Slf4j
public class ResponseWrapper extends HttpServletResponseWrapper {
/**
* @Description: 响应包装类
* @Date: 2020/5/26 16:29
*/
private ByteArrayOutputStream buffer = null;
private ServletOutputStream out = null;
private PrintWriter writer = null;
public ResponseWrapper(HttpServletResponse response) throws IOException {
super(response);
buffer = new ByteArrayOutputStream();// 真正存储数据的流
out = new WapperedOutputStream(buffer);
writer = new PrintWriter(new OutputStreamWriter(buffer, this.getCharacterEncoding()));
}
/**
* 重载父类获取outputstream的方法
*/
@Override
public ServletOutputStream getOutputStream() throws IOException {
return out;
}
/**
* 重载父类获取writer的方法
*/
@Override
public PrintWriter getWriter() throws UnsupportedEncodingException {
return writer;
}
/**
* 重载父类获取flushBuffer的方法
*/
@Override
public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
if (writer != null) {
writer.flush();
}
}
@Override
public void reset() {
buffer.reset();
}
/**
* 将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据
*/
public byte[] getResponseData() throws IOException {
flushBuffer();
return buffer.toByteArray();
}
/**
* 内部类,对ServletOutputStream进行包装
*/
private class WapperedOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos = null;
public WapperedOutputStream(ByteArrayOutputStream stream) {
bos = stream;
}
@Override
public void write(int b) throws IOException {
bos.write(b);
}
@Override
public void write(byte[] b) throws IOException {
bos.write(b, 0, b.length);
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setWriteListener(WriteListener writeListener) {
log.info("writeListener");
}
}
}
需要配置小程序appid和密钥,我是写在配置文件中的,需要获取配置文件参数的工具类看获取配置文件参数工具类