IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springBoot修改request的body实现CryptoJS前端加密后台Java解密 -> 正文阅读

[Java知识库]springBoot修改request的body实现CryptoJS前端加密后台Java解密

背景:客户端将加密的数据通过post请求发送到服务端,服务端修改request里的body,将解密后的body给到controller,服务器端请求处理后,统一在响应拦截处将加密后的数据返回给客户端

?1、加解密方法

package com.hi.hailiaowenan.base.utils;

import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

// CryptoJS前端加密后台Java解密
public class DesCipherUtil {
    private static String decryptKey = "aaaabbbbccccdddd";
    private static String encryptKey = "aaaabbbbccccdddd";
    /**
     * AES解密
     * @param encryptStr 密文
     * @param decryptKey 秘钥,必须为16个字符组成
     * @return 明文
     * @throws Exception
     */
    public static String aesDecrypt(String encryptStr) throws Exception {
        if (StringUtils.isEmpty(encryptStr) || StringUtils.isEmpty(decryptKey)) {
            return null;
        }
        byte[] encryptByte = Base64.getDecoder().decode(encryptStr);
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES"));
        byte[] decryptBytes = cipher.doFinal(encryptByte);
        return new String(decryptBytes);
    }

    /**
     * AES加密
     * @param content 明文
     * @param encryptKey 秘钥,必须为16个字符组成
     * @return 密文
     * @throws Exception
     */
    public static String aesEncrypt(String content) throws Exception {
        if (StringUtils.isEmpty(content) || StringUtils.isEmpty(encryptKey)) {
            return null;
        }

        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));

        byte[] encryptStr = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptStr);
     }

     // 测试加密与解密
    //  public static void main(String[] args) {
    //     try {
    //         String secretKey = "aaaabbbbccccdddd";
    //         String content = "{ a: 1, b: 2}";
    //         String s1 = aesEncrypt(content, secretKey);
    //         System.out.println(s1);
    //         String s = aesDecrypt(s1, secretKey);
    //         System.out.println(s);
    //     } catch (Exception e) {
    //         e.printStackTrace();
    //     }
    // }
}

2、通过自定义过滤器,实现controller层的接口解密,并将流继续保存下去

package com.hi.hailiaowenan.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSONObject;
import com.hi.hailiaowenan.base.bean.ReturnCode;
import com.hi.hailiaowenan.base.exception.UserException;
import com.hi.hailiaowenan.base.utils.DesCipherUtil;
import com.hi.hailiaowenan.wrapper.RequestWrapper;
import com.hi.hailiaowenan.wrapper.ResponseWrapper;

import org.apache.commons.lang.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Map;
 
@WebFilter(urlPatterns = "/*",filterName = "channelFilter")
// 通过自定义过滤器,将流继续保存下去 https://www.codenong.com/cs107098749/
public class ChannelFilter implements Filter {
 
    // 项目启动初始化的时候会被加载。
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
 
    }
 
    // 过滤请求,预处理。
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // filterChain.doFilter(servletRequest, servletResponse);
        // return;

        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;

        ResponseWrapper responseWrapper = null;

        String desnet = httpServletRequest.getHeader("Desnet"); // 是否需要加密
        String allparams = getBody(httpServletRequest);    // 请求body
        httpServletRequest = new RequestWrapper(httpServletRequest, allparams); 
        //判断body有值 且 需要加密 且 post请求
        if (StringUtils.isNotEmpty(desnet)) {
            if ("POST".equals(httpServletRequest.getMethod().toUpperCase())) {
                // 获取httpServletRequest的body参数
                if (StringUtils.isNotEmpty(allparams)) {
                    Map<String, Object> allparamsMap = JSONObject.parseObject(allparams);
                    Object aesparams = allparamsMap.get("params");
                    if (aesparams != null) {
                        String desparams = "";
                        try {
                            desparams = DesCipherUtil.aesDecrypt(aesparams.toString());
                        } catch (Exception e) {
                            throw new UserException(ReturnCode.BAD_REQUEST, "解密出错");
                        }
                        JSONObject jsStr = JSONObject.parseObject(desparams);
                        String ts = jsStr.toJSONString();
                        // 将参数放入重写的方法中
                        httpServletRequest = new RequestWrapper(httpServletRequest, ts);
                        responseWrapper = new ResponseWrapper(httpServletResponse);
                        filterChain.doFilter(httpServletRequest, responseWrapper); // 执行过滤
                    }else{
                        filterChain.doFilter(httpServletRequest, servletResponse); // 执行过滤
                    }
                }else{
                    filterChain.doFilter(httpServletRequest, servletResponse); // 执行过滤
                }
            } else{
                filterChain.doFilter(httpServletRequest, servletResponse); // 执行过滤
            }
        }else{
            filterChain.doFilter(httpServletRequest, servletResponse); // 执行过滤
        }
        
        //判断是否有值 且 需要加密 且 post请求
        if(responseWrapper != null){
            byte[] contentByte = responseWrapper.getContent();  // 获取返回值
            if (contentByte.length > 0 && StringUtils.isNotEmpty(desnet) && "POST".equals(httpServletRequest.getMethod().toUpperCase())) {
                String result = new String(contentByte, "UTF-8");
                String encryptStr = "";
                try {
                    encryptStr = DesCipherUtil.aesEncrypt(result); // 加密
                } catch (Exception e) {
                    throw new UserException(ReturnCode.BAD_REQUEST, "加密出错");
                }
                // 把返回值输出到客户端 这里用的 servletResponse,比较特殊
                ServletOutputStream out = servletResponse.getOutputStream();
                out.write(encryptStr.getBytes());
                out.flush();
            }
        }
        
        // filterChain.doFilter(request, responseWrapper); // 执行过滤
    }

    //获取Request的body数据
    private String getBody(ServletRequest request) {
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {

        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return stringBuilder.toString();
    }
    
    /**
     * 哪些路径不处理
     * @param request
     * @param strArr
     * @return
     */
    public boolean isIgnore(HttpServletRequest request,String[] strArr) {
        String path = request.getRequestURI();
        for(String ignore:strArr) {
            if(path.contains(ignore)) {
                return true;
            }
        }
        return false;
    }

    // 项目停止前,会执行该方法。
    @Override
    public void destroy() {
 
    }
}

3、?重写HttpServletRequestWrapper方法,由于HttpServletRequest中的body数据只能get不能set,即不能重新赋值,并且只能读取一次,但是我们的场景却是需要对body重新赋值,这个时候需要我们想办法重写

package com.hi.hailiaowenan.wrapper;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

// 重写HttpServletRequestWrapper方法
public class RequestWrapper extends HttpServletRequestWrapper {
    private String body;

    public RequestWrapper(HttpServletRequest request, String context) {
        super(request);
        body = context;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setReadListener(ReadListener readListener) {
            }
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    public String getBody() {
        return this.body;
    }

    public void setBodyJsonStr(String bodyJsonStr) {
        this.body = bodyJsonStr;
    }
}

4、?重写HttpServletResponseWrapper?方法

package com.hi.hailiaowenan.wrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

// 重写HttpServletResponseWrapper方法
public class ResponseWrapper extends HttpServletResponseWrapper {

    private ByteArrayOutputStream buffer;
    private ServletOutputStream out;

    public ResponseWrapper(HttpServletResponse httpServletResponse) {
        super(httpServletResponse);
        buffer = new ByteArrayOutputStream();
        out = new WrapperOutputStream(buffer);
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return out;
    }

    @Override
    public void flushBuffer() throws IOException {
        if (out != null) {
            out.flush();
        }
    }

    public byte[] getContent() throws IOException {
        flushBuffer();
        return buffer.toByteArray();
    }

    private static class WrapperOutputStream extends ServletOutputStream {
        private ByteArrayOutputStream bos;

        WrapperOutputStream(ByteArrayOutputStream bos) {
            this.bos = bos;
        }

        @Override
        public void write(int b)
                throws IOException {
            bos.write(b);
        }

        @Override
        public boolean isReady() {

            // TODO Auto-generated method stub
            return false;

        }

        @Override
        public void setWriteListener(WriteListener arg0) {

            // TODO Auto-generated method stub

        }
    }
}

4、在启动类中注入

@SpringBootApplication
@ServletComponentScan  //注册过滤器注解 对应filter/ChannelFiliter

完整代码?

package com.hi.hailiaowenan;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.hi.hailiaowenan.base.config.AliConfig;
import com.hi.hailiaowenan.base.config.QqConfig;
import com.hi.hailiaowenan.base.config.WxConfig;
import com.hi.hailiaowenan.base.config.ToutiaoConfig;

@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
@EnableConfigurationProperties({WxConfig.class, AliConfig.class, ToutiaoConfig.class, QqConfig.class})
@ComponentScan(basePackages = "com.hi.hailiaowenan.**")
@MapperScan("com.hi.hailiaowenan.**.**.mapper")
@SpringBootApplication
@ServletComponentScan  //注册过滤器注解 对应filter/ChannelFiliter
public class HailiaowenanApplication {
    public static void main(String[] args) {
        SpringApplication.run(HailiaowenanApplication.class, args);
    }
    
    //	redis 乱码序列化、
    @Bean(name = "redisTemplate")
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, String> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

至此,完成了客户端加密,服务器端解密参数,并将加密后的数据返回给客户端

参考文章:

SpringBoot通过拦截器获取请求参数和返回结果进行操作 | 码农家园

CryptoJS前端加密后台Java解密 - 简书

springboot过滤器中将form表单和body(json)形式的进参拦截修改里面参数内容(重写HttpServletRequestWrapper里边的方法)_酸菜鱼(szy)的博客-CSDN博客_formbody json

重写Request,修改Request中的Body内容 - 简书

SpringBoot实现数据加密传输_酷酷小饼干的博客-CSDN博客_springboot 加密传输???????

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-29 11:57:32  更:2022-04-29 12:01:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:18:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码