news 2026/1/15 22:27:22

vue大文件上传的加密传输安全性分析与提升

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
vue大文件上传的加密传输安全性分析与提升

涉密大文件传输系统设计方案

系统概述

作为四川某军工单位的技术负责人,针对政府单位涉密项目的大文件传输需求,我将设计一个基于国密算法SM4的安全文件传输系统。该系统需要满足10G级别文件传输、文件夹上传下载、服务端加密存储等核心功能,同时兼容主流浏览器和信创国产化环境。

技术选型分析

在前期调研中,我们发现现有开源解决方案存在以下问题:

  1. 百度WebUploader已停更,存在安全隐患
  2. 其他开源组件技术支持不足
  3. 缺乏完整的国密算法SM4集成方案
  4. 难以满足源代码审查要求

因此,我们决定基于现有技术栈自主开发核心组件:

  • 后端:SpringBoot + 达梦数据库
  • 前端:Vue CLI + 自主开发的上传组件
  • 加密:集成国密算法SM4

系统架构设计

前端实现 (Vue CLI)

// src/utils/sm4Encryptor.js - SM4加密工具类import{SM4}from'gm-crypt'exportdefaultclassSM4Encryptor{constructor(key){if(!key||key.length!==16){thrownewError('SM4 key must be 16 bytes')}this.sm4=newSM4({mode:'cbc',// 使用CBC模式iv:'0000000000000000',// 初始化向量padding:'pkcs#7'})this.sm4.setKey(key,'hex')}// 加密方法encrypt(data){if(typeofdata==='string'){returnthis.sm4.encrypt(data,'base64')}thrownewError('Only string encryption is supported')}// 解密方法decrypt(data){returnthis.sm4.decrypt(data,'base64','utf8')}// 文件分块加密asyncencryptFileChunk(chunk,progressCallback){returnnewPromise((resolve)=>{constreader=newFileReader()reader.onload=(e)=>{constencrypted=this.sm4.encrypt(e.target.result,'base64')if(progressCallback){progressCallback()}resolve(encrypted)}reader.readAsBinaryString(chunk)})}}

文件上传组件核心代码

// src/components/SecureFileUploader.vueimportSM4Encryptorfrom'@/utils/sm4Encryptor'importaxiosfrom'axios'exportdefault{name:'SecureFileUploader',props:{uploadUrl:{type:String,required:true},chunkSize:{type:Number,default:5*1024*1024// 5MB分块大小},sm4Key:{type:String,required:true}},data(){return{fileList:[],encryptor:null}},created(){this.encryptor=newSM4Encryptor(this.sm4Key)},methods:{triggerFileInput(){this.$refs.fileInput.click()},asynchandleFileChange(e){constitems=e.target.filesif(!items||items.length===0)return// 处理文件和文件夹for(leti=0;i<items.length;i++){constfile=items[i]constrelativePath=file.webkitRelativePath||file.name// 添加到文件列表constfileItem={id:this.generateFileId(),file,relativePath,size:file.size,progress:0,status:'pending',chunks:Math.ceil(file.size/this.chunkSize)}this.fileList.push(fileItem)// 开始上传awaitthis.uploadFile(fileItem)}},asyncuploadFile(fileItem){fileItem.status='uploading'constfile=fileItem.fileconstchunkSize=this.chunkSizeconsttotalChunks=fileItem.chunksfor(letchunkIndex=0;chunkIndex<totalChunks;chunkIndex++){conststart=chunkIndex*chunkSizeconstend=Math.min(start+chunkSize,file.size)constchunk=file.slice(start,end)try{// 加密文件分块constencryptedChunk=awaitthis.encryptor.encryptFileChunk(chunk,()=>{fileItem.progress=((chunkIndex+1)/totalChunks)*100})// 上传加密后的分块constformData=newFormData()formData.append('file',newBlob([encryptedChunk]))formData.append('fileName',file.name)formData.append('relativePath',fileItem.relativePath)formData.append('chunkIndex',chunkIndex)formData.append('totalChunks',totalChunks)formData.append('fileId',fileItem.id)formData.append('fileSize',file.size)awaitaxios.post(this.uploadUrl,formData,{headers:{'Content-Type':'multipart/form-data'},onUploadProgress:(progressEvent)=>{// 更新进度constchunkProgress=(progressEvent.loaded/progressEvent.total)*100fileItem.progress=((chunkIndex*100+chunkProgress)/totalChunks)}})}catch(error){console.error(`上传分块${chunkIndex+1}/${totalChunks}失败:`,error)fileItem.status='error'return}}// 所有分块上传完成fileItem.status='success'this.$emit('upload-complete',fileItem)},generateFileId(){return'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,function(c){constr=Math.random()*16|0constv=c==='x'?r:(r&0x3|0x8)returnv.toString(16)})},formatSize(bytes){if(bytes===0)return'0 Bytes'constk=1024constsizes=['Bytes','KB','MB','GB']consti=Math.floor(Math.log(bytes)/Math.log(k))returnparseFloat((bytes/Math.pow(k,i)).toFixed(2))+' '+sizes[i]}}}

后端实现 (SpringBoot)

// 文件上传控制器@RestController@RequestMapping("/api/files")publicclassFileUploadController{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(FileUploadController.class);@Value("${file.upload-dir}")privateStringuploadDir;@Value("${sm4.encrypt-key}")privateStringsm4Key;privatefinalSM4Utilsm4Util;privatefinalFileMetadataRepositoryfileMetadataRepository;publicFileUploadController(SM4Utilsm4Util,FileMetadataRepositoryfileMetadataRepository){this.sm4Util=sm4Util;this.fileMetadataRepository=fileMetadataRepository;}@PostMapping("/upload")publicResponseEntityuploadFile(@RequestParam("file")MultipartFilefile,@RequestParam("fileName")StringfileName,@RequestParam("relativePath")StringrelativePath,@RequestParam("chunkIndex")intchunkIndex,@RequestParam("totalChunks")inttotalChunks,@RequestParam("fileId")StringfileId,@RequestParam("fileSize")longfileSize){try{// 创建文件存储目录PathuploadPath=Paths.get(uploadDir).toAbsolutePath().normalize();if(!Files.exists(uploadPath)){Files.createDirectories(uploadPath);}// 处理分块文件StringtempFileName=fileId+".part";PathtempFilePath=uploadPath.resolve(tempFileName);// 解密文件分块 (服务端也需要支持SM4解密)byte[]decryptedBytes=sm4Util.decrypt(file.getBytes(),sm4Key);// 写入分块文件try(OutputStreamout=Files.newOutputStream(tempFilePath,chunkIndex>0?StandardOpenOption.APPEND:StandardOpenOption.CREATE)){out.write(decryptedBytes);}// 如果是最后一个分块,合并文件if(chunkIndex==totalChunks-1){PathfinalFilePath=uploadPath.resolve(relativePath);Files.createDirectories(finalFilePath.getParent());// 重命名临时文件为最终文件名Files.move(tempFilePath,finalFilePath,StandardCopyOption.REPLACE_EXISTING);// 保存文件元数据到达梦数据库FileMetadatametadata=newFileMetadata();metadata.setFileId(fileId);metadata.setFileName(fileName);metadata.setRelativePath(relativePath);metadata.setFileSize(fileSize);metadata.setUploadTime(LocalDateTime.now());metadata.setStoragePath(finalFilePath.toString());fileMetadataRepository.save(metadata);logger.info("文件 {} 上传完成",relativePath);}returnResponseEntity.ok().build();}catch(Exceptione){logger.error("文件上传失败: {}",e.getMessage(),e);returnResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Collections.singletonMap("error","文件上传失败"));}}}

SM4加密工具类 (Java)

// SM4加密工具类@ComponentpublicclassSM4Util{publicbyte[]encrypt(byte[]data,StringhexKey)throwsException{// 验证密钥长度if(hexKey==null||hexKey.length()!=32){thrownewIllegalArgumentException("SM4 key must be 16 bytes (32 hex characters)");}// 创建SM4加密器 (使用CBC模式)SM4Enginesm4Engine=newSM4Engine();CBCBlockCiphercbcBlockCipher=newCBCBlockCipher(sm4Engine);// 初始化向量 (16字节的0)byte[]iv=newbyte[16];ParametersWithIVparametersWithIV=newParametersWithIV(newKeyParameter(Hex.decode(hexKey)),iv);// 创建缓冲加密器BufferedBlockCiphercipher=newPaddedBufferedBlockCipher(cbcBlockCipher,newPKCS7Padding());cipher.init(true,parametersWithIV);// 执行加密byte[]output=newbyte[cipher.getOutputSize(data.length)];intlength=cipher.processBytes(data,0,data.length,output,0);length+=cipher.doFinal(output,length);// 返回实际加密后的数据returnArrays.copyOf(output,length);}publicbyte[]decrypt(byte[]encryptedData,StringhexKey)throwsException{// 验证密钥长度if(hexKey==null||hexKey.length()!=32){thrownewIllegalArgumentException("SM4 key must be 16 bytes (32 hex characters)");}// 创建SM4解密器 (使用CBC模式)SM4Enginesm4Engine=newSM4Engine();CBCBlockCiphercbcBlockCipher=newCBCBlockCipher(sm4Engine);// 初始化向量 (16字节的0)byte[]iv=newbyte[16];ParametersWithIVparametersWithIV=newParametersWithIV(newKeyParameter(Hex.decode(hexKey)),iv);// 创建缓冲解密器BufferedBlockCiphercipher=newPaddedBufferedBlockCipher(cbcBlockCipher,newPKCS7Padding());cipher.init(false,parametersWithIV);// 执行解密byte[]output=newbyte[cipher.getOutputSize(encryptedData.length)];intlength=cipher.processBytes(encryptedData,0,encryptedData.length,output,0);length+=cipher.doFinal(output,length);// 返回实际解密后的数据returnArrays.copyOf(output,length);}}

系统安全设计

  1. 传输安全

    • 所有数据传输均采用SM4加密
    • 分块上传机制减少单次传输数据量
    • 支持断点续传
  2. 存储安全

    • 服务端存储加密文件
    • 文件元数据存储在达梦数据库
    • 严格的访问权限控制
  3. 密钥管理

    • 使用硬件安全模块(HSM)管理主密钥
    • 每次传输使用临时会话密钥
    • 密钥交换采用SM2国密算法

信创环境兼容性

  1. 操作系统:支持麒麟、统信UOS等国产操作系统
  2. 浏览器:兼容360安全浏览器、红芯浏览器等信创浏览器
  3. 数据库:全面适配达梦数据库
  4. 中间件:支持东方通、宝兰德等国产中间件

源代码审查准备

为满足政府单位源代码审查要求,我们将:

  1. 提供完整的前后端源代码
  2. 包含详细的开发文档和注释
  3. 提供加密算法实现的白皮书
  4. 准备第三方安全审计报告
  5. 建立源代码版本管理系统

后续工作计划

  1. 完成核心功能开发并进行单元测试
  2. 进行系统集成测试和性能测试
  3. 申请国密局相关安全认证
  4. 准备源代码审查材料
  5. 部署到测试环境进行用户验收测试

该方案完全自主可控,所有核心代码均可提供源代码审查,满足政府单位对信息安全的高标准要求。

将组件复制到项目中

示例中已经包含此目录

引入组件

配置接口地址

接口地址分别对应:文件初始化,文件数据上传,文件进度,文件上传完毕,文件删除,文件夹初始化,文件夹删除,文件列表
参考:http://www.ncmem.com/doc/view.aspx?id=e1f49f3e1d4742e19135e00bd41fa3de

处理事件

启动测试

启动成功

效果

数据库

效果预览

文件上传

文件刷新续传

支持离线保存文件进度,在关闭浏览器,刷新浏览器后进行不丢失,仍然能够继续上传

文件夹上传

支持上传文件夹并保留层级结构,同样支持进度信息离线保存,刷新页面,关闭页面,重启系统不丢失上传进度。

批量下载

支持文件批量下载

下载续传

文件下载支持离线保存进度信息,刷新页面,关闭页面,重启系统均不会丢失进度信息。

文件夹下载

支持下载文件夹,并保留层级结构,不打包,不占用服务器资源。

下载示例

点击下载完整示例

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/13 16:18:27

基于PLC的S7-200 MCGS矿井通风安全智能控制系统

S7-200 MCGS 基于PLC的矿井通风安全控制系统矿下通风系统这玩意儿就像煤矿的肺管子&#xff0c;得24小时不间断运转。传统继电器控制那套动不动就烧触点&#xff0c;去年在山西某矿上亲眼见过老控制柜里继电器炸出火星子&#xff0c;跟放炮似的。现在搞PLCMCGS的组合拳才是真香…

作者头像 李华
网站建设 2026/1/11 10:13:10

数学竞赛党必备:VibeThinker-1.5B-APP精准求解AIME难题

数学竞赛党必备&#xff1a;VibeThinker-1.5B-APP精准求解AIME难题 在当今AI技术飞速发展的背景下&#xff0c;大模型动辄千亿参数、训练成本动用数百万美元已成常态。然而&#xff0c;在数学竞赛领域&#xff0c;一个仅15亿参数、总训练成本不到8000美元的小模型&#xff0c;却…

作者头像 李华
网站建设 2026/1/15 19:35:47

申威SW64架构军用级系统:高安全环境模型部署设想

申威SW64架构军用级系统&#xff1a;高安全环境模型部署设想 在野战指挥所的昏暗灯光下&#xff0c;一名参谋军官正面对复杂的兵力调度问题——如何在有限时间内完成多目标路径规划&#xff1f;传统算法工具响应迟缓&#xff0c;而依赖外部云服务又存在数据泄露风险。此时&…

作者头像 李华
网站建设 2026/1/15 4:27:30

联通云AI开发套件:是否预装类似轻量推理模型?

联通云AI开发套件是否适合预装轻量推理模型&#xff1f;从VibeThinker-1.5B看“小模型高性能”的落地可能 在当前大模型军备竞赛愈演愈烈的背景下&#xff0c;百亿、千亿参数的庞然大物不断刷新性能上限。但对大多数开发者而言&#xff0c;真正需要的往往不是一个能写诗又能编程…

作者头像 李华
网站建设 2026/1/11 23:46:50

【Docker日志轮转最佳实践】:避免磁盘爆满的5个关键配置步骤

第一章&#xff1a;Docker日志轮转的核心意义在容器化部署日益普及的今天&#xff0c;Docker应用产生的日志数据量呈指数级增长。若缺乏有效的管理机制&#xff0c;单个容器的日志文件可能迅速膨胀&#xff0c;占用大量磁盘空间&#xff0c;甚至导致主机系统因磁盘满载而无法响…

作者头像 李华
网站建设 2026/1/14 17:48:05

配置文件写不对,服务崩溃没人知?Docker健康检查你必须掌握的3个关键点

第一章&#xff1a;配置错误引发的服务隐患在现代分布式系统中&#xff0c;服务的稳定运行高度依赖于精确的配置管理。一个微小的配置失误&#xff0c;例如端口冲突、路径错误或权限设置不当&#xff0c;可能导致服务无法启动、数据泄露甚至整个系统瘫痪。常见配置陷阱 环境变量…

作者头像 李华