添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
注册/登录

使用Jsencrypt配合axios实现数据传输加密

安全 应用安全
对称加密就是两边拥有相同的秘钥,两边都知道如何将密文加密解密。这种加密方式固然很好,但是问题就在于如何让双方知道秘钥。

[[404720]]

本文转载自微信公众号「粥里有勺糖」,作者粥里有勺糖。转载本文请联系粥里有勺糖公众号。

不希望应用发送的数据能在 Devtools 中被看到,避免接口被“同行”扒下来,然后被恶意使用

要避免此问题,首先想到的就是对传输的数据进行一次加密,让后端自行解密然后处理

尽管js源码是被浏览器公开的,但通过构建工具混淆后,在没有source map的情况下还不不易定位目标代码

期望加密后的样子传输的内容如下

加密方案简述

对称加密就是两边拥有相同的秘钥,两边都知道如何将密文加密解密。

这种加密方式固然很好,但是问题就在于如何让双方知道秘钥。

由于传输数据都是走的网络,如果将秘钥通过网络的方式传递的话,一旦秘钥被截获就没有加密的意义

非对称加密

有公钥私钥之分:

  • 公钥所有人都可以知道,可以将数据用公钥加密,但是将数据解密必须使用私钥解密
  • 私钥只有分发放公钥的一方才知道
  • 这种加密方式就可以完美解决对称加密存在的问题

    通过对比,选用保密性好的 非对称加密 方案作为加密方案

    本文选用 RSA[1] 对称加密算法

    公私钥生成

    根据百度经验的建议,生成一个1024位的的秘钥

    这里使用openssl生成,window下建议在Git Bash下使用

    1. openssl genrsa -out rsa_1024_priv.pem 1024 
    1. openssl rsa -pubout -in rsa_1024_priv.pem -out rsa_1024_pub.pem 

    jsencrypt

  • jsencrypt[2]
  • nodejs-jsencrypt[3]
  • “使用 Javascript 进行RSA加密的解决方案

    1. # web 
    2. npm i jsencrypt 
    3.  
    4. # node 
    5. npm i nodejs-jsencrypt 
    1. // web 
    2. import JSEncrypt from 'jsencrypt' 
    3.  
    4. // node 
    5. const { JSEncrypt } = require('nodejs-jsencrypt'

    公钥加密方法

    1. // 上述自动生成 
    2. const pubKey = '上述生成的公钥' 
    3.  
    4. function publicEncrypt(str){ 
    5.     const encrypt = new JSEncrypt() 
    6.     encrypt.setPublicKey(pubKey) 
    7.     return encrypt.encrypt(str) 

    私钥解密方法

    1. const privKey = `上述生成的私钥` 
    2.  
    3. function privDecrypt(str) { 
    4.     const encrypt = new JSEncrypt() 
    5.     encrypt.setPrivateKey(privKey) 
    6.     return encrypt.decrypt(str) 

    可以看出API非常简洁

    1. let str = publicEncrypt('hello world'
    2. console.log(str) 
    3. console.log(privDecrypt(str)) 

    结合Axios实践

    Axios配置

    1. npm i axios 

    将加密逻辑放入到axios的请求拦截器中,将原内容使用 JSON.stringify处理后再进行加密,加密后的内容使用value属性传递,如下所示

    1. import axios from "axios"
    2.  
    3. // 引入刚刚编写的加密方法 
    4. import { publicEncrypt } from "./utils/crypto"
    5.  
    6. const http = axios; 
    7. http.defaults.baseURL = '/api' 
    8. http.defaults.headers = { 
    9.   "content-Type""application/json" 
    10. }; 
    11.  
    12. // 请求拦截器 
    13. http.interceptors.request.use( 
    14.   config => { 
    15.     // 发送之前操作config 
    16.     // 对传递的 data 进行加密 
    17.     config.data = { 
    18.       value:publicEncrypt(JSON.stringify(config.data)) 
    19.     } 
    20.     return config; 
    21.   }, 
    22.   err => { 
    23.     // 处理错误 
    24.     return Promise.reject(err); 
    25.   } 
    26. ); 
    27. http.interceptors.response.use( 
    28.   response => { 
    29.     // 返回前操作 
    30.     return response.data; 
    31.   }, 
    32.   err => { 
    33.     return Promise.reject(err); 
    34.   } 
    35. ); 
    36.  
    37. export default http; 

    服务端解密示例代码

    这里列举了两种,一种直接使用Node.js的http模块编写,一种使用Express编写:

  • 解密收到的内容
  • 将解密后的内容直接返回
  • http模块示例

    使用data事件与end事件配合,接收传递的数据,然后进行解密返回

    1. const http = require('http'
    2.  
    3. // 引入解密方法 
    4. const { privDecrypt } = require('./utils/crypto'
    5.  
    6. const server = http.createServer((req, res) => { 
    7.     res.setHeader('content-type','application/json'
    8.     let buffer = Buffer.alloc(0) 
    9.  
    10.     // 接收传递的数据 
    11.     req.on('data',(chunk)=>{ 
    12.         buffer = Buffer.concat([buffer, chunk]) 
    13.     }) 
    14.     req.on('end',()=>{ 
    15.         try { 
    16.             // 解密传递的数据 
    17.             const data = privDecrypt(JSON.parse(buffer.toString('utf-8')).value) 
    18.             res.end(data) 
    19.         } catch (error) { 
    20.             console.log(error); 
    21.             res.end('error')             
    22.         } 
    23.     }) 
    24. }) 
    25.  
    26. // 启动 
    27. server.listen(3000, err => { 
    28.     console.log(`listen 3000 success`); 
    29. }) 

    Express示例

    配置一个前置的*路由,解密传递的内容,然后将其重新绑定到req.body上,供后续其它路由使用

    1. const express = require('express'
    2. const { privDecrypt } = require('./utils/crypto'
    3.  
    4. const server = express() 
    5.  
    6. server.use(express.urlencoded({ extended: false })) 
    7. server.use(express.json({ strict: true })) 
    8.  
    9. // 首先进入的路由 
    10. server.route('*').all((req, res, next) => { 
    11.     console.log(`${req.method}--${req.url}`) 
    12.     req.body = JSON.parse(privDecrypt(req.body.value)) 
    13.     next() 
    14. }) 
    15.  
    16. server.post('/test/demo',(req,res)=>{ 
    17.     // 直接返回实际的内容 
    18.     res.json(req.body) 
    19. }) 
    20.  
    21. // 启动 
    22. server.listen(3000, err => { 
    23.     console.log(`listen 3000 success`); 
    24. }) 

    前端代码示例

    使用了 Vite 作为开发预览工具

    vite.config.js配置: 只做了请求代理,解决开发跨域问题

    1. export default { 
    2.     server: { 
    3.         proxy: { 
    4.             '/api': { 
    5.                 target: 'http://localhost:3000'
    6.                 changeOrigin: true
    7.                 rewrite: (path) => path.replace(/^\/api/, ''
    8.             }, 
    9.         } 
    10.     } 
    1. <body> 
    2.     <button id="send">发送</button> 
    3.     <hr> 
    4.     <h2></h2> 
    5.     <textarea id="receive" placeholder="接收的内容"></textarea> 
    6.     <script type="module" src="./index.js"></script> 
    7. </body> 
    1. import $http from './http' 
    2. const $send = document.getElementById('send'
    3. const $receive = document.getElementById('receive'
    4.  
    5. $send.addEventListener('click',function(){ 
    6.     // 发送一个随机内容 
    7.     $http.post('/test/demo',{ 
    8.         name:'xm'
    9.         age:~~(Math.random()*1000) 
    10.     }).then((res)=>[ 
    11.         updateReceive(res) 
    12.     ]) 
    13. }) 
    14.  
    15. function updateReceive(data){ 
    16.     $receive.value = data instanceof Object?JSON.stringify(data):data 

    发送网络请求

    请求响应内容

    大功告成,接入十分简单

    完整的示例代码仓库[4]

    [1]RSA: https://baike.baidu.com/item/RSA%E7%AE%97%E6%B3%95/263310

    [2]jsencrypt: https://www.npmjs.com/package/jsencrypt

    [3]nodejs-jsencrypt: https://www.npmjs.com/package/nodejs-jsencrypt

    [4]完整的示例代码仓库: https://github.com/ATQQ/demos/tree/main/asymmetric-encryption

    责任编辑:武晓燕 粥里有勺糖
    点赞
    收藏