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站。

浙公网安备 33010602011771号