• crypto-js前端实现加密学习日志(—项目实践)


    1、由于项目需要,对文件进行加密,然后上传至阿里oss。出于后端带宽压力,在前端进行加密。由于加密过程比较耗时,容易阻塞主进程,所以决定使用worker来进行。

    废话不多说,直接上代码。

    2、首先是utils.ts,主要是封装一些加密、解密、通用工具类。

    import CryptoJs, {WordArray, AES} from 'crypto-js';
    /**
     * 加密函数使用的CryptoJs的AES/CBC/pkcs7进行加密
     * @param {*} key 加密用的秘钥,由于项目中的key使用了base64编码,所以需要解码
     * @param {*} content 要加密的内容,CryptoJs接收WordArray | string
     * @returns res返回值为加密后的WordArray, 大端序。
     */ 
    export const encode = (key: string, content: string | WordArray) => {
      const encodeKey = CryptoJs.enc.Base64.parse(key)
      const ivWords = [...encodeKey.words].splice(0, 4)
      const iv = CryptoJs.lib.WordArray.create(ivWords)
      const res = AES.encrypt(content, encodeKey, {
        iv: iv,
        mode: CryptoJs.mode.CBC,
        padding: CryptoJs.pad.Pkcs7
      }).ciphertext
      return res
    }
    
    /**
     * 
     * @param {*} array 要初始化为WordArray的typedArray
     * @returns res返回值为WordArray(Crypto使用的WordArray)
     */
    export type typedArray = String | ArrayBuffer | Int32Array | Uint32Array | Int16Array | Uint16Array
            | Int8Array | Uint8ClampedArray | Uint8Array | Float32Array | Float64Array
    export const createWordArray = (array: typedArray) => {
      return CryptoJs.lib.WordArray.create(array)
    }
    
    /**
     * @param {*} array 要解密的WordArray
     * @returns res返回值为CipherParams(CryptoJS解密用的)
     */
     export const formatCipher = (array: WordArray) => {
       return CryptoJs.lib.CipherParams.create({ciphertext: array})
     }
    
     /**
      * 解密函数
      * @param {*} key //解密时候用的key
      * @param {*} content // 要解密的内容(CryptoJS接收string | WordArray)
      * @returns res // 解密后的文件内容,通过base64编码
      */
     export const decode = (key: string, content: string | WordArray) => {
      const decodeKey = CryptoJs.enc.Base64.parse(key)
      const ivWords = [...decodeKey.words].splice(0, 4)
      const iv = CryptoJs.lib.WordArray.create(ivWords)
      var res = AES.decrypt(content, decodeKey, {
        iv: iv,
        mode: CryptoJs.mode.CBC,
        padding: CryptoJs.pad.Pkcs7
      }).toString(CryptoJs.enc.Base64)
      return res
     }
    /**
     * WordArray 转化为DataView
     * @param wordArray 
     * @returns DataView
     */
     export const wAToB = (wordArray: WordArray) => {
      const buffer = new ArrayBuffer(wordArray.sigBytes)
      let resView = new DataView(buffer)
      for(let i = 0; i < wordArray.words.length; i++) {
        resView.setInt32(i * 4, wordArray.words[i])
      }
      return resView
    }

    2.encode.worker.ts这是加密的worker线程,

    import {encode, createWordArray, wAToB} from '../utils/utils';
    const enWorker: Worker = self as any;
    enWorker.onmessage =  function(e) {
      const {file, tokenInfo} = e.data
      const reader = new FileReader();
      reader.onload = function() {
        const { key, fileName } = tokenInfo;
        let content = reader.result;
        content = createWordArray(content);
        content = encode(key, content);
        const res = wAToB(content)
        const resFile = new File([res], fileName, {type: file.type})
        enWorker.postMessage({content: resFile, done: true})
      }
      reader.readAsArrayBuffer(file)
    }
    
    export default null as any

    3、decode.worker.ts解密的worker线程

    import {decode, formatCipher, createWordArray} from '../utils/utils';
    const deWorker: Worker = self as any;
    deWorker.onmessage = function(e) {
      const {file, key, type} = e.data
      let content = createWordArray(file)
      content = formatCipher(content)
      const res = decode(key, content)
      const resStr = `data:${type};base64,${res}`
      deWorker.postMessage({content: resStr, done: true})
    }
    export default null as any

    4、创建index.tsx。把worker.ts引入,然后实例化。

    import React, {useState} from 'react';
    import './index.css';
    import EncodeWorker from '../workers/encode.worker';
    import DecodeWorker from '../workers/decode.worker';
    
    function EncodePage() {
      
      const [fileList, setFileList] = useState([])
    
      const changeFile = (files: FileList) => {
        console.log(files)
        const file = files[0]
        if(!file) {
          return
        }
        if(file.size > 1024*1024*50) {
          return alert('文件太大')
        }
        changeFileList({
          id: `-${file.lastModified}`,
          name: file.name,
          content: null
        })
        encodeFile(file)
      }
    
      const changeFileList = (file) => {
        const ind = fileList.findIndex(
          item =>
            item.id === file.id
        )
        if(ind === -1) {
          fileList.push(file)
        } else {
          fileList.splice(ind, 1, file)
        }
        setFileList([...fileList])
      }
      // 加密
      const encodeFile = (file: File) => {
        const encodeWorker = new EncodeWorker();
        encodeWorker.postMessage({file, tokenInfo: {fileName: file.name, key: 'axyIIVwxqRnPnqc9RDRzXg=='}})
        encodeWorker.onmessage = function(e) {
          let {content, done} = e.data;
          if(done){
            encodeWorker.terminate()
            changeFileList({
              id: `-${file.lastModified}`,
              name: file.name,
              content: content
            })
            console.log('end-encode', new Date().getTime())
          }
        }
      }
      //解密
      const decodeFile = (file: String | ArrayBuffer, name: String, type: String) => {
        const decodeWorker = new DecodeWorker();
        decodeWorker.postMessage({file, key: 'axyIIVwxqRnPnqc9RDRzXg==', type})
        decodeWorker.onmessage = function(e) {
          let {content, done} = e.data;
          if(done){
            decodeWorker.terminate()
            console.log('end-decode', new Date().getTime())
            downloadFile(content, name)
          } else {
            console.log(content)
          }
        }
      }
    
      const downloadFile = (url, name) => {
        let a = document.createElement('a')
        a.href = `${url}`
        a.target = "__blank"
        a.download = name || ""
        a.click()
      }
    
      const downEncodeFile = (file) => {
        // console.log(file)
        const reader = new FileReader()
        reader.onload = function() {
          // console.log(reader.result)
          downloadFile(reader.result, file.name)
        }
        reader.readAsDataURL(file)
      }
    
      const downDecodeFile = (file) => {
        const reader = new FileReader()
        reader.onload = function() {
          decodeFile(reader.result, file.name, file.type)
        }
        reader.readAsArrayBuffer(file)
      }
    
      return (
        <div className="upload-file">
          <div className="file-btn">
            <span>选择文件</span>
            <input type="file" onChange={(e) => changeFile(e.target.files)} />
          </div>
          <div className="file-list">
            {
              fileList.map((v) => {
                return (
                  <div key={v.id} className="file-item">
                    {v.name}
                    ({v.content?'加密完成':'加密中...'})
                    <span onClick={() => downEncodeFile(v.content)} className="download-btn">下载加密文件</span>
                    <span onClick={() => downDecodeFile(v.content)} className="decode-btn">下载解密文件</span>
                  </div>
                )
              })
            }
          </div>
        </div>
      )
    }
    
    export default EncodePage

    5、在线测试demo请移动我的github站

  • 相关阅读:
    大数据量分表时 两个表查询比较快的方式
    开启SQL Server执行占用时间显示和逻辑读取次数显示
    【转】SQL Server海量数据库的索引、查询优化及分页算法
    Exchange无法发送邮件 未找到匹配的连接器来路由外部收件人解决办法
    HTML介绍&常用的标签
    关于HTML文件、JS文件、CSS文件
    python命名空间和作用域
    pymysql
    存储过程、视图、触发器、函数
    多表查询
  • 原文地址:https://www.cnblogs.com/marvey/p/12933632.html
Copyright © 2020-2023  润新知