过滤器作为全局加解密的用法案例

基于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;
        }
    }
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

specialApe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值