zoukankan      html  css  js  c++  java
  • 前vue后springboot端统一修改请求与响应中的标头及body的方法(双向加解密数据传输)

    晕死,直接上代码了还不能放首页,如果这样都不行,那不写了

    一、结果

     

      

    二、过程

    vue 端 request.js 核心代码,加解密工具类,请见前面的贴子

      1 // request拦截器
      2 service.interceptors.request.use(config => {
      3   // 是否需要设置 token
      4   const isToken = (config.headers || {}).isToken === false
      5   // 是否需要防止数据重复提交
      6   const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
      7   if (getToken() && !isToken) {
      8     config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
      9   }
     10   // get请求映射params参数
     11   if (config.method === 'get' && config.params) {
     12     let url = config.url + '?' + tansParams(config.params)
     13     url = url.slice(0, -1)
     14     config.params = {}
     15     config.url = url
     16   }
     17 
     18   // 1.动态生成密钥 des3 key
     19   const DES3_KEY_SIZE = 24
     20   let des3Key = genKey(DES3_KEY_SIZE, true)
     21   // console.log("des3Key:",des3Key);
     22 
     23   // 2.key 经过rsa ,服务器端公钥加密,再base64, 得到密文key:SecurityKey
     24   let SecurityKey = RsaEncrypts(des3Key, true, true, false)
     25 
     26   // 3.设置header:SecurityKey
     27   config.headers['SecurityKey'] = SecurityKey
     28   // console.log("SecurityKey:",SecurityKey);
     29 
     30   // console.log("config.data:",config.data);
     31   let plainData = JSON.stringify(config.data)
     32   // console.log("plainData:", plainData);
     33   let cipherData = ''
     34   // 4.将传输body数据des3加密,生成密文body,再base64,再urlcode
     35   if (config.data != 'undefined') {
     36     cipherData = encrypt3DES('TripleDES', plainData, des3Key, CryptoJS.mode.CBC, CryptoJS.pad.Pkcs7, true, true, false)
     37     //  console.log("cipherData:", cipherData);
     38   }
     39 
     40   // 5. 将密文body,经过base64, SHA256 生成摘要,客户端私钥生成签名,再base64
     41   let Authentication = RsaSignAuthentication(cipherData, 'PKCS#7', 'SHA256withRSA', true, true, false)
     42 
     43   // 6.设置header:Authentication
     44   config.headers['Authentication'] = Authentication
     45   // console.log("Authentication:",Authentication);
     46   // 7.避免特殊字符丢失 urlcode
     47   cipherData = encodeURIComponent(cipherData)
     48   config.data = cipherData
     49 
     50   if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
     51     let requestObj = {
     52       url: config.url,
     53       data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
     54       time: new Date().getTime()
     55     }
     56     // console.log("发送数据:",requestObj.data);
     57 
     58     const sessionObj = cache.session.getJSON('sessionObj')
     59     if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
     60       cache.session.setJSON('sessionObj', requestObj)
     61     } else {
     62       const s_url = sessionObj.url                  // 请求地址
     63       const s_data = sessionObj.data                // 请求数据
     64       const s_time = sessionObj.time                // 请求时间
     65       const interval = 1000                         // 间隔时间(ms),小于此时间视为重复提交
     66       if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
     67         const message = '数据正在处理,请勿重复提交'
     68         console.warn(`[${s_url}]: ` + message)
     69         return Promise.reject(new Error(message))
     70       } else {
     71         cache.session.setJSON('sessionObj', requestObj)
     72       }
     73     }
     74   }
     75   return config
     76 }, error => {
     77   console.log(error)
     78   Promise.reject(error)
     79 })
     80 
     81 // 响应拦截器
     82 service.interceptors.response.use(response => {
     83     // 未设置状态码则默认成功状态
     84     const code = response.data.code || 200
     85     // 获取错误信息
     86     const message = errorCode[code] || response.data.message || errorCode['default']
     87 
     88     // 注意服务器端有大写字符,但前端接收返回全部小写
     89     let authentication = response.headers['authentication']
     90     let securityKey = response.headers.securitykey
     91     // 不要用console.log("响应结果:"+response.headers); 返回[object object]
     92     // console.log("响应body:",response.data);
     93 
     94     if (authentication) {
     95       let cipherData = decodeURIComponent(response.data)
     96       // console.log('接收到服务器端返回body密文:', cipherData)
     97       let verifyAuthentication = RsaVerifyAuthentication(cipherData, authentication, 'PKCS#7', 'MD5withRSA', true,
     98         true, false)
     99       //console.log('通过服务器端的公钥,验签结果:' + verifyAuthentication.toString())
    100       if (verifyAuthentication) {
    101         // let key = '1234567890123456'
    102         // let aesKey = Base64.encode(key);
    103 
    104         //  aesKey ="8A5EzyHtJjklPj4a9NWwXw==";
    105         // console.log('测试,得到aeskey:' + aesKey)
    106 
    107         let aesKey = RsaDecrypts(securityKey, true, true, false)
    108         // console.log('通过客户端的私钥解密得到明文key:', aesKey)
    109 
    110         //  let plain = '依芸 abcdef 123'
    111         // let cipherData = encryptAES('AES', plain, aesKey, CryptoJS.mode.CBC, CryptoJS.pad.Pkcs7, true, true, false)
    112         // console.log('测试,加密得到密文数据:', cipherData)
    113 
    114         // let plainData = decryptAES('AES', cipherData, aesKey, CryptoJS.mode.CBC, CryptoJS.pad.Pkcs7, true, true, false)
    115         // console.log('测试,解密得到明文数据:', plainData)
    116 
    117         let plainData = decryptAES('AES', cipherData, aesKey, CryptoJS.mode.CBC, CryptoJS.pad.Pkcs7, true, true, false)
    118         // console.log('通过解密密钥后再aes解密body密文得到明文body:', plainData)
    119 
    120         response.data = JSON.parse(plainData)
    121         // 转换成json对象
    122       }
    123     }
    124 
    125     // 二进制数据则直接返回
    126     if (response.request.responseType === 'blob' || response.request.responseType === 'arraybuffer') {
    127       return response.data
    128     }
    129 
    130     if (code === 401) {
    131       if (!isReloginShow) {
    132         isReloginShow = true
    133         MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
    134             confirmButtonText: '重新登录',
    135             cancelButtonText: '取消',
    136             type: 'warning'
    137           }
    138         ).then(() => {
    139           isReloginShow = false
    140           store.dispatch('LogOut').then(() => {
    141             // 如果是登录页面不需要重新加载
    142             if (window.location.hash.indexOf('#/login') != 0) {
    143               location.href = '/index'
    144             }
    145           })
    146         }).catch(() => {
    147           isReloginShow = false
    148         })
    149       }
    150       return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
    151     } else if (code === 500) {
    152       Message({
    153         message: message,
    154         type: 'error'
    155       })
    156       return Promise.reject(new Error(message))
    157     } else if (code !== 200) {
    158       Notification.error({
    159         title: message
    160       })
    161       return Promise.reject('error')
    162     } else {
    163       return response.data
    164     }
    165   },
    166   error => {
    167     console.log('err' + error)
    168     let { message } = error
    169     if (message == 'Network Error') {
    170       message = '后端接口连接异常'
    171     } else if (message.includes('timeout')) {
    172       message = '系统接口请求超时'
    173     } else if (message.includes('Request failed with status code')) {
    174       message = '系统接口' + message.substr(message.length - 3) + '异常'
    175     }
    176     Message({
    177       message: message,
    178       type: 'error',
    179       duration: 5 * 1000
    180     })
    181     return Promise.reject(error)
    182   }
    183 )
    View Code

    spring boot 端 核心代码  加解密工具类,请见前面的贴子

    DecodeRequestBodyAdvice.java

      1 package com.ruoyi.framework.interceptor;
      2 
      3 import com.ruoyi.common.constant.Constants;
      4 import com.ruoyi.common.utils.enDeCrypt.RsaUtil;
      5 import com.ruoyi.common.utils.enDeCrypt.TripleDesUtil;
      6 import com.ruoyi.common.utils.enDeCrypt.UrlCode;
      7 import lombok.SneakyThrows;
      8 import org.springframework.core.MethodParameter;
      9 import org.springframework.http.HttpHeaders;
     10 import org.springframework.http.HttpInputMessage;
     11 import org.springframework.http.converter.HttpMessageConverter;
     12 import org.springframework.web.bind.annotation.ControllerAdvice;
     13 import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
     14 
     15 import java.io.*;
     16 import java.lang.reflect.Type;
     17 import java.nio.charset.StandardCharsets;
     18 
     19 /**
     20  * @author x8023z
     21  */
     22 @ControllerAdvice(basePackages = "com.ruoyi.web.controller")
     23 public class DecodeRequestBodyAdvice implements RequestBodyAdvice {
     24     @Override
     25     public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
     26         //true开启功能,false关闭这个功能
     27         return true;
     28     }
     29 
     30     @Override
     31     public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
     32         return body;
     33     }
     34 
     35     //在读取请求之前处理
     36     @SneakyThrows
     37     @Override
     38     public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
     39         if (httpInputMessage.getBody().available() <= 0) {
     40             return httpInputMessage;
     41         }
     42         String SecurityKey = httpInputMessage.getHeaders().get("SecurityKey").get(0);
     43         //System.out.println("SecurityKey:"+SecurityKey);
     44         String Authentication = httpInputMessage.getHeaders().get("Authentication").get(0);
     45         //  System.out.println("Authentication:"+Authentication);
     46 
     47         byte[] requestDataByte = new byte[httpInputMessage.getBody().available()];
     48         httpInputMessage.getBody().read(requestDataByte);
     49         byte[] requestDataByteNew = null;
     50         try {
     51             // 解密
     52             requestDataByteNew = this.decryptBody(requestDataByte, SecurityKey, Authentication);
     53         } catch (IOException e) {
     54             throw new RuntimeException("解密异常");
     55         }
     56         // 使用解密后的数据,构造新的读取流
     57         InputStream rawInputStream = new ByteArrayInputStream(requestDataByteNew);
     58         return new HttpInputMessage() {
     59             @Override
     60             public HttpHeaders getHeaders() {
     61                 return httpInputMessage.getHeaders();
     62             }
     63 
     64             @Override
     65             public InputStream getBody() throws IOException {
     66                 return rawInputStream;
     67             }
     68         };
     69     }
     70 
     71 
     72     /**
     73      * 在Http消息转换器执转换,之后执行
     74      * body 转换后的对象
     75      * inputMessage 客户端的请求数据
     76      * parameter handler方法的参数类型
     77      * targetType handler方法的参数类型
     78      * converterType 使用的Http消息转换器类类型
     79      *
     80      * @return 返回一个新的对象
     81      */
     82     @Override
     83     public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
     84         return body;
     85     }
     86 
     87 
     88     /**
     89      * SHA256签名验证,DES3解密
     90      *
     91      * @param requestBytes
     92      * @return
     93      */
     94     public byte[] decryptBody(byte[] requestBytes, String SecurityKey, String Authentication) throws Exception {
     95         byte[] byteResult = new byte[0];
     96         if (requestBytes.length <= 0) {
     97             return byteResult;
     98         }
     99 
    100         //1. urldecode 得到base64 body
    101         String requestData = new String(requestBytes, StandardCharsets.UTF_8);
    102         UrlCode urlCode = new UrlCode();
    103         String urlDecodeData = urlCode.decodeURL(requestData, StandardCharsets.UTF_8).trim();
    104         // System.out.println("密文body:"+urlDecodeData);
    105 
    106         RsaUtil rsaUtil = null;
    107         String des3Key = "";
    108 
    109         rsaUtil = new RsaUtil(true, Constants.RSA_CBC_PKCS7, Constants.SHA256WITHRSA);
    110         boolean isVerifyPassed = rsaUtil.verifySignPublicKey(urlDecodeData, Authentication, Constants.PUBLICKEY_C, true, true, false);
    111         //System.out.println("isVerifyPassed:"+isVerifyPassed);
    112 
    113         //2.先sha256withrsa 验证签名再解密密钥,再des3解密数据
    114         String plainBody = "";
    115         if (isVerifyPassed) {
    116             rsaUtil = new RsaUtil(true);
    117             //3.解密des3key  rsa RSA_PKCS1_PADDING 解密
    118             des3Key = rsaUtil.decryptByPrivateKey(SecurityKey, Constants.PRRIVATEKEY_S, true, true, false);
    119             // System.out.println("des3Key:"+des3Key);
    120             if (des3Key != null) {
    121                 //4. des3解密body
    122                 TripleDesUtil tripleDes = new TripleDesUtil(urlDecodeData, Constants.DESEDE, des3Key, Constants.DES3_KEY_SIZE, Constants.DES3_IV, Constants.DES3_CBC_PKCS7, Constants.UTF8, true, true);
    123                 plainBody = tripleDes.decode();
    124                 // System.out.println("plainBody:"+plainBody);
    125                 //验证通过,返回解密后的参数
    126                 return plainBody.getBytes(StandardCharsets.UTF_8);
    127             } else {
    128                 //解密des3key异常
    129                 throw new RuntimeException("解密密钥异常");
    130             }
    131         } else {
    132             throw new RuntimeException("验签异常");
    133             //验签异常
    134         }
    135     }
    136 }
    View Code

    EncodeResponseBodyAdvice.java

     1 package com.ruoyi.framework.interceptor;
     2 
     3 import com.alibaba.fastjson.JSON;
     4 import com.alibaba.fastjson.serializer.SerializerFeature;
     5 import com.ruoyi.common.constant.Constants;
     6 import com.ruoyi.common.utils.enDeCrypt.AesUtil;
     7 import com.ruoyi.common.utils.enDeCrypt.RsaUtil;
     8 import lombok.SneakyThrows;
     9 import org.springframework.core.MethodParameter;
    10 import org.springframework.http.MediaType;
    11 import org.springframework.http.server.ServerHttpRequest;
    12 import org.springframework.http.server.ServerHttpResponse;
    13 import org.springframework.web.bind.annotation.ControllerAdvice;
    14 import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
    15 import java.net.URLEncoder;
    16 
    17 /**
    18  * @author EvianZou
    19  */
    20 @ControllerAdvice(basePackages = "com.ruoyi.web.controller")
    21 public class EncodeResponseBodyAdvice implements ResponseBodyAdvice {
    22 
    23     @Override
    24     public boolean supports(MethodParameter methodParameter, Class aClass) {
    25         return true;
    26     }
    27 
    28     @SneakyThrows
    29     @Override
    30     public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
    31         String body = JSON.toJSONString(o, SerializerFeature.DisableCircularReferenceDetect);
    32 
    33         RsaUtil rsaUtil = new RsaUtil(true);
    34         AesUtil aesUtil = new AesUtil();
    35 
    36         // 密钥:动态生成AESKey,RSA客户端公钥加密,再base64
    37         rsaUtil.setSignAlgorithm(Constants.SHA1WITHRSA);
    38         rsaUtil.setPaddingMode(Constants.RSA_ECB_PKCS1);
    39 
    40         String aesKey = aesUtil.getAesSecretKey(Constants.AES, Constants.PASSWORD, true);
    41         String securityKey = rsaUtil.encryptByPublicKey(aesKey, Constants.PUBLICKEY_C, true, true, false);
    42         // System.out.println("通过客户端公钥rsa加密密钥得到密文key:" + securityKey);
    43 
    44         // 服务器端作为发送方
    45         // 签名 传输数据转json字符串,base64,MD5摘要,RSA服务器端私钥生成签名,base64
    46         rsaUtil.setSignAlgorithm(Constants.MD5WITHRSA);
    47         rsaUtil.setPaddingMode(Constants.RSA_ECB_PKCS7);
    48 
    49         // 密钥、密文 是否utf8,是否base64,是否hex 都影响前后端加解密是否一致。jdk1.8以下版本默认不支持aes密钥长度为256
    50         String cipherData = aesUtil.encode(body, aesKey, Constants.AES_CBC_PKCS7, Constants.AES_IV, true, true, false);
    51         // System.out.println("通过明文key加密body得到密文body:"+cipherData);
    52 
    53         /*
    54         //String  plainText = aesUtil.decode(cipherText,aesKey,Constants.AES_CBC_PKCS7,Constants.AES_IV,true,true,false);
    55         //System.out.println("通过明文key解密body得到明文body:" + plainText);
    56 
    57         // aesKey="1234567890123456";
    58         // aesKey = DatatypeConverter.printBase64Binary(aesKey.getBytes(StandardCharsets.UTF_8));
    59         //  aesKey = "8A5EzyHtJjklPj4a9NWwXw==";
    60         // System.out.println("测试,得到aesKey:" + aesKey);
    61 
    62         // String test = aesUtil.encode("邹依芸 abcdef 123",aesKey,Constants.AES_CBC_PKCS7,Constants.AES_IV,true,true,false);
    63         // System.out.println("测试,加密得到密文数据:" + test);
    64 
    65         //  test = aesUtil.decode(test,aesKey,Constants.AES_CBC_PKCS7,Constants.AES_IV,true,true,false);
    66         // System.out.println("测试,解密得到明文数据:" + test);
    67         */
    68 
    69         String authentication = rsaUtil.signByPrivateKey(cipherData, Constants.PRRIVATEKEY_S, true, true, false);
    70         // System.out.println("通过服务器端的私钥key生成摘要得到签名" + authentication);
    71 
    72         serverHttpResponse.getHeaders().set("Access-Control-Expose-Headers", "Authentication,SecurityKey");
    73 
    74         // 密钥:动态生成AESKey,RSA客户端公钥加密,再base64
    75         serverHttpResponse.getHeaders().set("SecurityKey", securityKey);
    76 
    77         // 服务器端作为发送方
    78         // 签名 传输数据转json字符串,base64,MD5摘要,RSA服务器端私钥生成签名
    79         serverHttpResponse.getHeaders().set("Authentication", authentication);
    80 
    81         // 不直接返回加密字符串,因为控制层使用了@ResponseBody注解,直接返回字符串会在前后各多出一个双引号
    82 
    83         // 解决数据传输丢失特殊符号问题:如url特殊字符, 丢失+号,+号变成了空格
    84         return URLEncoder.encode(cipherData,Constants.UTF8);
    85     }
    86 
    87 }
    View Code

    三、原理

    1、springboot: 切面

    RequestBodyAdvice 可以理解为在 @RequestBody 之前需要进行操作,ResponseBodyAdvice 可以理解为在 @ResponseBody 之后进行的操作,所以当接口需要加解密时,在使用 @RequestBody 接收前台参数之前可以先在 RequestBodyAdvice 的实现类中进行参数的解密,当操作结束需要返回数据时,可以在@ResponseBody之后进入ResponseBodyAdvice的实现类中进行参数的加密。

    2、Vue中使用axios封装的全局拦截器,拦截器(英文:Interceptors)会在每次发起 ajax 请求和得到响应的时候自动被触发。

    3、加解密逻辑说明

  • 相关阅读:
    ASP.NET读入文件(以txt为例)
    二叉树的相关规律公式
    HashTable的一点儿常识
    ASP.NET页面跳转方式及页面传值方式
    事件的那些事儿
    程序集和项目、命名空间、动态链接库的区别
    EventHandlerList的一些发现
    《Ajax基础教程》电子版下载
    数据库优化设计
    创业者的自我训练:
  • 原文地址:https://www.cnblogs.com/zjp8023/p/15792244.html
Copyright © 2011-2022 走看看