基于spring boot下的filter配置如下
解密的requestwrapper的实质是重写过滤器的方法达到全局的效果:
/**
* 全局解密的filter
* @package: com.i2f.training.comm.filter
* @date: 2020/3/3 21:44
*/
@Slf4j
@WebFilter(urlPatterns = "/*",filterName = "decryptFilter")
@Order(1)
public class CryptoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
log.info("全局请求解密过滤器,初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
/** 解密 1.get,delete 参数是查询参数,是在URL后面,通过getParameter()获取 2.post,put 参数是在请求体中,通过inputStream来获取 */
/** stream流只能读取一次,使用StreamUtils.copyToString() 时,就是进行了read操作 */
log.info("全局请求解密过滤器,正在执行");
filterChain.doFilter(new TrainingRequestWrapper((HttpServletRequest) servletRequest), servletResponse);
}
@Override
public void destroy() {
log.info("全局请求解密过滤器,被销毁");
}
}
重写http下的requestwrapper用到了加解密的工具类在下列列出
/**
* 请求wrapper包装类, 对请求发送来数据统一解密
* @package: com.i2f.training.comm.wrapper
* @date: 2020/3/3 21:48
*/
@Slf4j
public class TrainingRequestWrapper extends HttpServletRequestWrapper {
public TrainingRequestWrapper(HttpServletRequest request) { super(request); }
/**
* struts2框架中get请求使用该方法
* @date 2020/3/7 19:03
* @author XuQ
* @return java.util.Map<java.lang.String,java.lang.String[]>
*/
@Override
public Map<String, String[]> getParameterMap() {
boolean flag = true;
Map<String, String[]> parameterMap = super.getParameterMap();
log.info("RequestWrapper: " + Charset.defaultCharset());
//todo 1.我这里弄了一个map
Map<String, String[]> map = new HashMap<>();
Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
for (Map.Entry<String, String[]> next : entries) {
String key = next.getKey();
String[] value = next.getValue();
//todo 2.我这里弄了一个字符数组
String[] stampValueArray = new String[value.length];
for (int i = 0; i < value.length; i++) {
if (flag) {
try {
//todo 3.我将解码以后的值放入我自己弄的字符数组里面
stampValueArray[i] = new String(CryptoUtils.decrypt(value[i]).getBytes("GBK"), StandardCharsets.UTF_8);
log.info("字符的转换次数:" + i + "转换后的值:" + value[i]);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
flag = false;
}
}
//todo 4.将key,和我自己弄的数组添加到map中
map.put(key, stampValueArray);
}
//todo 5.将map返回
return map;
}
@SneakyThrows
@Override
public String getParameter(String name) { return new String(CryptoUtils.decrypt(super.getParameter(name)).getBytes("GBK"), StandardCharsets.UTF_8); }
/**
* get请求 查询字符串使用该方法
* @date 2020/3/7 18:1
* @param name 键名(键是指URL后参数 --> 键=值&键=值)
* @return java.lang.String[]
*/
@Override
public String[] getParameterValues(String name) {
HttpServletRequest request = (HttpServletRequest) super.getRequest();
String signature = request.getHeader("signature");
//todo 请求头有无签名
if (StringUtils.isBlank(signature)) {
log.warn("请求头中没有signature,无法验签,非法访问!");
throw new TrainingException("999999","签名为空,非法访问!");
}
String queryString = request.getQueryString();
String md5DigestAsHex = DigestUtils.md5DigestAsHex(queryString.getBytes());
//todo 验签
if (!StringUtils.equals(md5DigestAsHex, signature)) {
log.warn("请求头与查询字符串不匹配,验签失败,非法访问!");
throw new TrainingException("999999","验签失败,非法访问!");
}
String[] parameterValues = super.getParameterValues(name);
for (int i = 0; i < parameterValues.length; i++) {
String decrypt = CryptoUtils.decrypt(parameterValues[i]);
/** 前端发送的形如
* name=GYQWju49pkZvArTpWqpHLUmd%2BV4uSDL0eqgNKDgohoo/kHtDC1pt8gEIuaXHsRY9&age=FZ61BASb/hLQm0xc0T7sHCKN/xSIV//s%2Bkl7FX3R2MY=
* 在解析时会在数据外额外加一个""
* 这个进行处理,将 " 去掉
**/
String replaced = decrypt.replaceAll("\"", "");
parameterValues[i] = replaced;
}
return parameterValues;
}
/**
* post请求 获取请求体中流数据,全加密
* @date 2020/3/7 18:13
* @author XuQ
* @param
* @return javax.servlet.ServletInputStream
*/
@Override
public ServletInputStream getInputStream() throws IOException {
ServletInputStream inputStream = super.getInputStream();
String signature = ((HttpServletRequest) super.getRequest()).getHeader("signature");
if (shouldDecrypt()) {
if (StringUtils.isBlank(signature)) {
log.warn("请求头中没有signature,无法验签,非法访问!");
throw new TrainingException("999999","签名为空,非法访问!");
}
/* 前端传来的解密前的JSON串 */
String unDecrypt = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
if (!StringUtils.equals(DigestUtils.md5DigestAsHex(unDecrypt.getBytes()), signature)) {
log.warn("请求头中签名与信息加密后不匹配,验签失败,非法访问!");
throw new TrainingException("999999","验签失败,非法访问!");
}
//todo 验签成功进行解密
String decrypted = CryptoUtils.decrypt(unDecrypt);
InputStream byteArrayInputStream = new ByteArrayInputStream(decrypted != null ? decrypted.getBytes() : new byte[0]);
return new ServletInputStream() {
@Override
public boolean isFinished() { return !isReady(); }
@Override
public boolean isReady() {
try {
return byteArrayInputStream.available() > 0;
} catch (IOException e) {
log.warn("requestWrapper里,请求体中无数据");
e.printStackTrace();
return false;
}
}
@Override
public void setReadListener(ReadListener readListener) { }
@Override
public int read() throws IOException { return byteArrayInputStream.read(); }
};
} else {
return inputStream;
}
}
/** 只有当URI的访问请求是我们想要的请求时,才使用重写的方法,不然我们不使用 */
private boolean shouldDecrypt() { return true; }
}
用到的工具类进行加解密
/**
* 加、解密工具类
*
* @package: com.i2f.training.common.util
* @author: 第零组---XuQ
* @date: 2020/3/7 20:54
*/
public class CryptoUtils {
/**
* 加解密秘钥 偏移量
*/
public interface CryptoParams {
/**
* 加密密钥
*/
String key = "e2b8b25fad71409e";
/**
* 偏移量
*/
String iv = "9e69e212bb37b587";
}
/**
* 加密
*
* @param data 需加密数据
* @return java.lang.String
* @date 2020/3/9 12:09
* @author XuQ
*/
public static String encrypt(String data) {
try {
String key = CryptoParams.key;
String iv = CryptoParams.iv;
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes();
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encrypted = cipher.doFinal(plaintext);
String resultStr = new BASE64Encoder().encode(encrypted);
resultStr = resultStr.replaceAll("\\+", "%2B");
return resultStr;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 解密
*
* @param data 需解密数据
* @return java.lang.String
* @date 2020/3/9 12:09
* @author XuQ
*/
public static String decrypt(String data) {
try {
if (data == null) {
return null;
} else if ("".equals(data)) {
return "";
} else {
data = data.replaceAll("%2B", "+");
String key = CryptoParams.key;
String iv = CryptoParams.iv;
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(data);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original).trim();
return originalString;
}
} catch (Exception e) {
return data;
}
}
}