• 传输加解密 RuoYi-Vue-PLus 4.x


    前言

    RuoYi-VUE-Plus 5.x的版本 已经完成了 前端加密传输给后端的代码,本文章只是将代码迁移到4.x 并完成 后端加密后给前端的代码,具体内容和加密方式都是使用作者:疯狂的狮子Li,欢迎大家使用

    代码修改

    前端

    一、前端相关代码

    引入步骤:

    1. 文件替换:将Utils 下面的代码进行替换,根据原有模块的功能 需要加入crypto、jsencrypt、request几个文件
    2. 配置密钥:jsencrypt里面的公钥和私钥需要和后端的配置文件一致起来
    3. 引入依赖:package.json文件下 引入npm 依赖 “crypto-js”: “^4.1.1”,

    代码解释:

    **request**: 请求文件,里面包含了请求加密和返回参数解密,通过判断header头中是否存在固定值来确定
    **crypto** :AES、Base64 加解密文件主要是对参数进行加解密的方式
    **jsencrypt**: RSA加解密文件 主要是对密钥进行加解密
    
    • 1
    • 2
    • 3

    密钥加密流程
    1. 生成32位AES密码
    2. Base64 加密AES密码
    3. 利用RSA将 加密后的AES密码进行加密
    4. 传输给后端
    数据加密流程
    使用32位AES密码作为key,加密数据 encryptWithAes(JSON.stringify(config.data), aesKey)

    代码

    package.json

    "crypto-js": "^4.1.1",
    
    • 1

    src/utils 下 crypto.js

    import CryptoJS from 'crypto-js';
    
    /**
     * 随机生成32位的字符串
     * @returns {string}
     */
    const generateRandomString = () => {
        const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
        let result = '';
        const charactersLength = characters.length;
        for (let i = 0; i < 32; i++) {
            result += characters.charAt(Math.floor(Math.random() * charactersLength));
        }
        return result;
    };
    
    /**
     * 随机生成aes 密钥
     * @returns {string}
     */
    export const generateAesKey = () => {
        return CryptoJS.enc.Utf8.parse(generateRandomString());
    };
    
    /**
     * 加密base64
     * @returns {string}
     */
    export const encryptBase64 = (str) => {
        return CryptoJS.enc.Base64.stringify(str);
    };
    
    /**
     * 解密base64
     * @returns {string}
     */
    export const decryptBase64 = (str) => {
        var words = CryptoJS.enc.Base64.parse(str);
        return CryptoJS.enc.Utf8.stringify(words);
    };
    
    /**
     * 使用密钥对数据进行加密
     * @param message
     * @param aesKey
     * @returns {string}
     */
    export const encryptWithAes = (message, aesKey) => {
        const encrypted = CryptoJS.AES.encrypt(message, aesKey, {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        });
        return encrypted.toString();
    };
    
    /**
     * 使用密钥对数据进行加密
     * @param message
     * @param aesKey
     * @returns {string}
     */
    export const decryptWithAes = (message, aesKey) => {
        const keyBytes = CryptoJS.enc.Utf8.parse(aesKey);
        const decrypt = CryptoJS.AES.decrypt(message, keyBytes, {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        });
        return decrypt.toString(CryptoJS.enc.Utf8)
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    3.jsencrypt.js

    import JSEncrypt from 'jsencrypt/bin/jsencrypt.min'
    
    // 密钥对生成 http://web.chacuo.net/netrsakeypair
    
    const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' +
      'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQdAQ==自己生成密钥对 这里的我删除了'
    
    const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' +
      '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' +
      'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' +
      'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' +
      'UP8iWi1Qw0Y='
    
    // 加密
    export function encrypt(txt) {
      const encryptor = new JSEncrypt()
      encryptor.setPublicKey(publicKey) // 设置公钥
      return encryptor.encrypt(txt) // 对数据进行加密
    }
    
    // 解密
    export function decrypt(txt) {
      const encryptor = new JSEncrypt()
      encryptor.setPrivateKey(privateKey) // 设置私钥
      return encryptor.decrypt(txt) // 对数据进行解密
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    4.request.js

    import axios from 'axios'
    import {Loading, Message, MessageBox, Notification} from 'element-ui'
    import store from '@/store'
    import {getToken} from '@/utils/auth'
    import errorCode from '@/utils/errorCode'
    import {blobValidate, tansParams} from "@/utils/ruoyi";
    import cache from '@/plugins/cache'
    import {saveAs} from 'file-saver'
    import {decrypt, encrypt} from "@/utils/jsencrypt";
    import {decryptBase64, decryptWithAes, encryptBase64, encryptWithAes, generateAesKey} from '@/utils/crypto';
    
    let downloadLoadingInstance;
    // 是否显示重新登录
    export let isRelogin = {show: false};
    
    axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
    // 对应国际化资源文件后缀
    axios.defaults.headers['Content-Language'] = 'zh_CN'
    // 创建axios实例
    const service = axios.create({
        // axios中请求配置有baseURL选项,表示请求URL公共部分
        baseURL: process.env.VUE_APP_BASE_API, // 超时
        timeout: 10000
    })
    
    // request拦截器
    service.interceptors.request.use(config => {
        // 是否需要设置 token
        const isToken = (config.headers || {}).isToken === false
        // 是否需要防止数据重复提交
        const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
        // 是否需要加密
        const isEncrypt = (config.headers || {}).isEncrypt === true;
        if (getToken() && !isToken) {
            config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
        }
        // get请求映射params参数
        if (config.method === 'get' && config.params) {
            let url = config.url + '?' + tansParams(config.params);
            url = url.slice(0, -1);
            config.params = {};
            config.url = url;
        }
        if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
            const requestObj = {
                url: config.url, data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, time: new Date().getTime()
            }
            const sessionObj = cache.session.getJSON('sessionObj')
            if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
                cache.session.setJSON('sessionObj', requestObj)
            } else {
                const s_url = sessionObj.url;                  // 请求地址
                const s_data = sessionObj.data;                // 请求数据
                const s_time = sessionObj.time;                // 请求时间
                const interval = 1000;                         // 间隔时间(ms),小于此时间视为重复提交
                if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
                    const message = '数据正在处理,请勿重复提交';
                    console.warn(`[${s_url}]: ` + message)
                    return Promise.reject(new Error(message))
                } else {
                    cache.session.setJSON('sessionObj', requestObj)
                }
            }
        }
        // 当开启参数加密
        if (isEncrypt && (config.method === 'post' || config.method === 'put')) {
            // 生成一个 AES 密钥
            const aesKey = generateAesKey();
            console.log(aesKey)
            config.headers['encrypt-key'] = encrypt(encryptBase64(aesKey));
            config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey);
        }
        // FormData数据去请求头Content-Type
        if (config.data instanceof FormData) {
            delete config.headers['Content-Type'];
        }
        return config;
    }, error => {
        console.log(error)
        Promise.reject(error)
    })
    
    // 响应拦截器
    service.interceptors.response.use(res => {
        if (res.headers && res.headers['encrypt-key']) {
            const encryptByRsa = res.headers['encrypt-key']
            //取出aes 加密后的key 然后解密
            const encryptAesByBase64 = decrypt(encryptByRsa)
            const aesPassword = decryptBase64(encryptAesByBase64)  //eDBLQjEyQlBtZ1Y0ZjBLam9MZnJqUXNDd0I3dkJpenA=
            res.data = JSON.parse(decryptWithAes(res.data, aesPassword));
        }
        //解密出现错误转json 提示错误
        if (typeof res.data === 'string') {
            res.data = JSON.parse(res.data)
        }
        // 未设置状态码则默认成功状态
        const code = res.data.code || 200;
        // 获取错误信息
        const msg = errorCode[code] || res.data.msg || errorCode['default']
        // 二进制数据则直接返回
        if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
            return res.data
        }
        if (code === 401) {
            if (!isRelogin.show) {
                isRelogin.show = true;
                MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
                    confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning'
                }).then(() => {
                    isRelogin.show = false;
                    store.dispatch('LogOut').then(() => {
                        location.href = process.env.VUE_APP_CONTEXT_PATH + "index";
                    })
                }).catch(() => {
                    isRelogin.show = false;
                });
            }
            return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
        } else if (code === 500) {
            Message({message: msg, type: 'error'})
            return Promise.reject(new Error(msg))
        } else if (code === 601) {
            Message({message: msg, type: 'warning'})
            return Promise.reject('error')
        } else if (code !== 200) {
            Notification.error({title: msg})
            return Promise.reject('error')
        } else {
            return res.data
        }
    }, error => {
        console.log('err' + error)
        let {message} = error;
        if (message == "Network Error") {
            message = "后端接口连接异常";
        } else if (message.includes("timeout")) {
            message = "系统接口请求超时";
        } else if (message.includes("Request failed with status code")) {
            message = "系统接口" + message.substr(message.length - 3) + "异常";
        }
        Message({message: message, type: 'error', duration: 5 * 1000})
        return Promise.reject(error)
    })
    
    // 通用下载方法
    export function download(url, params, filename, config) {
        downloadLoadingInstance = Loading.service({text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)",})
        return service.post(url, params, {
            transformRequest: [(params) => {
                return tansParams(params)
            }], headers: {'Content-Type': 'application/x-www-form-urlencoded'}, responseType: 'blob', ...config
        }).then(async (data) => {
            const isBlob = blobValidate(data);
            if (isBlob) {
                const blob = new Blob([data])
                saveAs(blob, filename)
            } else {
                const resText = await data.text();
                const rspObj = JSON.parse(resText);
                const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
                Message.error(errMsg);
            }
            downloadLoadingInstance.close();
        }).catch((r) => {
            console.error(r)
            Message.error('下载文件出现错误,请联系管理员!')
            downloadLoadingInstance.close();
        })
    }
    
    export default service
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172

    上面的代码基本是作者自己的可以参考5.X的代码


    后端

    二、后端相关代码

    引入步骤:

    1. 文件替换:后端文件 包括:加解密方法、拦截器、配置文件,按照文件位置替换进去,配置文件中加了api-decrypt: 配置参数

    2. 配置密钥:可以通过互联网生成的key 需要与前端密钥相同,否则会导致无法解密

    代码解释:

    EncryptFilter :判断是否是排除加密路由,进行加密 具体看代码

    密钥加密流程

    1. 生成32位AES密码
    2. Base64 加密AES密码
    3. 利用RSA将 加密后的AES密码进行加密
    4. 传输给后端

    数据加密流程

    使用32位AES密码作为key,加密数据 EncryptUtils.encryptByAes(responseBody, aesPassword);
    
    • 1

    代码

    1.application.yml

    # api接口加密
    api-decrypt:
      # 是否开启全局接口加密
      enabled: true
      # AES 加密头标识
      headerFlag: encrypt-key
      # 公私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
      publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
      privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsWqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
      # 不需要加密路径
      encryptExcludes: /monitor/*,/tool/*,/captchaImage
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.过滤器

    package com.ruoyi.framework.encrypt.filter;//package com.yawei.gateway.filter;
    
    /**
     * 描述
     *
     * @author :suiquantong
     * @date : 2023/3/15 9:22
     */
    
    import cn.hutool.core.util.RandomUtil;
    import com.ruoyi.common.constant.HttpStatus;
    import com.ruoyi.common.utils.EncryptUtils;
    import com.ruoyi.common.utils.StringUtils;
    import com.ruoyi.framework.config.properties.ApiDecryptProperties;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.MediaType;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.nio.charset.StandardCharsets;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 解密拦过滤器
     *
     * @author yawei
     */
    public class EncryptFilter implements Filter {
        private final ApiDecryptProperties properties;
    
        /**
         * 排除链接
         */
        public List<String> excludes = new ArrayList<>();
    
        public EncryptFilter(ApiDecryptProperties properties) {
            this.properties = properties;
        }
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            String tempExcludes = filterConfig.getInitParameter("excludes");
            if (StringUtils.isNotEmpty(tempExcludes)) {
                String[] url = tempExcludes.split(StringUtils.SEPARATOR);
                for (int i = 0; url != null && i < url.length; i++) {
                    excludes.add(url[i]);
                }
            }
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
            HttpServletRequest servletRequest = (HttpServletRequest) request;
    
            String url = servletRequest.getServletPath();
            /*
             * 1.过滤不需要加密url (可选)
             * 2.过滤非Json 格式数据
             */
    
            if (!StringUtils.matches(url, excludes)) {
    //        if (false) {
                EncryptResponseWrapper responseWrapper = new EncryptResponseWrapper((HttpServletResponse) response);
                //执行业务逻辑 交给下一个过滤器或servlet处理
                chain.doFilter(request, responseWrapper);
                System.out.println("responseWrapper.getContentType() = " + responseWrapper.getContentType());
                if (!isJsonResponse(response)) {
                    System.out.println("response.getContentType() = " + response.getContentType());
                    //不需要加密
                    chain.doFilter(request, response);
                }else {
                    try {
                        //获取返回体
                        byte[] responseData = responseWrapper.getResponseData();
                        //设置响应内容格式,防止解析响应内容时出错
    //                responseWrapper.setContentType("text/plain;charset=UTF-8");
                        String responseBody = new String(responseData, StandardCharsets.UTF_8);
                        //生成aes密码 采用res加密  与前端相同逻辑
                        //生成aes 32位随机码,
                        String aesPassword = RandomUtil.randomString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 32);
                        //加密aes 密钥
                        String encryptAesByBase64 = EncryptUtils.encryptByBase64(aesPassword);
                        String encryptByRsa = EncryptUtils.encryptByRsa(encryptAesByBase64, properties.getPublicKey());
                        //绑定返回头标识
                        responseWrapper.setHeader(properties.getHeaderFlag(), encryptByRsa);
    
                        //加密内容
                        String encryptBody = EncryptUtils.encryptByAes(responseBody, aesPassword);
                        PrintWriter out = response.getWriter();
                        out.print(encryptBody);
                        out.flush();
                        out.close();
                    } catch (Exception e) {
                        try {
                            getFailResponse(responseWrapper);
                        } catch (IOException ioException) {
                            ioException.printStackTrace();
                        }
                        e.printStackTrace();
                    }
                }
    
    
            } else {
                //不需要加密
                chain.doFilter(request, response);
            }
    
    
        }
    
    
        /**
         * 有错误相应返回-44
         *
         * @param response
         * @throws IOException
         */
        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\":" + HttpStatus.ENCRYPT_FAIL + ",\n" + "    \"message\": 解密出现异常!,\n" + "    \"data\": []\n" + "}");
            //加密后的错误消息
    //        out.write("+D+JO8tuwkrNbxnTTLdqStifmQceT+LlYETnIG/JZKrbAn+gIiqIp3VbzBV1y6R8B7aY53VM2xHa7cY3Osbnqw==");
            out.flush();
            out.close();
        }
    
    
        /**
         * 是否是Json请求
         */
        public boolean isJsonResponse(ServletResponse response) {
            String contentType = response.getContentType();
            return StringUtils.startsWithIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    package com.ruoyi.framework.encrypt.filter;
    
    
    import cn.hutool.core.io.IoUtil;
    import com.ruoyi.common.constant.HttpStatus;
    import com.ruoyi.common.utils.EncryptUtils;
    
    import javax.servlet.ServletOutputStream;
    import javax.servlet.WriteListener;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpServletResponseWrapper;
    import java.io.*;
    import java.nio.charset.StandardCharsets;
    
    /**
     * @Description: 响应包装类
     * @Date: 2020/5/26 16:29
     */
    public class EncryptResponseWrapper extends HttpServletResponseWrapper {
        private ByteArrayOutputStream buffer = null;
        private ServletOutputStream out = null;
        private PrintWriter writer = null;
    
        public EncryptResponseWrapper(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)
                throws IOException {
                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) {
    
            }
        }
    
        public static   ByteArrayOutputStream toByteArray(InputStream input) throws IOException {
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024*4];
            int n = 0;
            while (-1 != (n = input.read(buffer))) {
    
                output.write(buffer, 0, n);
            }
            return output;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    3.具体内容太多了 直接上附件吧

  • 相关阅读:
    电脑自动关机是什么原因?解决方案全解析!
    基于android的移动学习平台(前端APP+后端Java和MySQL)
    jsp文件中模板字符串变量${}失效
    Redis的主从复制搭建
    [软件安装]anaconda安装
    【java养成】:main函数中String[] args的作用、商品入库案例、猜数字游戏、随机点名器
    Ansible_自动化运维实战(一)
    OceanBase 分布式数据库【信创/国产化】- OceanBase 概述
    关于K8s中资源服务质量管理Resource Qos的一些笔记整理
    [黑马程序员Pandas教程]——分组与分箱
  • 原文地址:https://blog.csdn.net/qq_36651772/article/details/133883330