news 2026/9/29 18:50:56

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/9/27 17:25:16

小参数大智慧:7800美元训练成本换来媲美GPT-OSS-20B的表现

小参数大智慧&#xff1a;7800美元训练成本换来媲美GPT-OSS-20B的表现 在当前AI模型“军备竞赛”愈演愈烈的背景下&#xff0c;动辄千亿参数、数百万美元训练预算的大模型似乎成了唯一的主流叙事。然而&#xff0c;当算力资源逐渐成为少数巨头的专属领地时&#xff0c;一个反向…

作者头像 李华
网站建设 2026/9/27 22:09:06

OpenCV图像处理流水线设计:输入需求输出Python调用链

VibeThinker-1.5B-APP&#xff1a;小模型如何在编程与数学推理中超越大模型&#xff1f; 当我们在准备一场算法竞赛&#xff0c;面对一道复杂的动态规划题时&#xff0c;是否曾希望有一个“外脑”能快速给出解题思路&#xff1f;或者在深夜调试代码时&#xff0c;渴望一个不依…

作者头像 李华
网站建设 2026/9/26 19:08:02

GitHub镜像推荐:一键部署VibeThinker-1.5B-APP进行高效算法推理

GitHub镜像推荐&#xff1a;一键部署VibeThinker-1.5B-APP进行高效算法推理 在当前大模型动辄数百亿、数千亿参数的浪潮中&#xff0c;一个仅15亿参数的小模型却悄然在数学与代码推理领域掀起波澜——VibeThinker-1.5B-APP。它没有华丽的通用对话能力&#xff0c;也不擅长写诗…

作者头像 李华
网站建设 2026/9/26 20:07:58

CPU和内存总是爆满?,深度解析Docker资源限制与调优策略

第一章&#xff1a;CPU和内存爆满的根源剖析在高并发或资源管理不当的系统中&#xff0c;CPU和内存使用率飙升是常见且棘手的问题。其根本原因往往涉及程序逻辑缺陷、系统配置不足以及外部负载异常等多个层面。深入分析这些因素&#xff0c;有助于快速定位并解决性能瓶颈。资源…

作者头像 李华
网站建设 2026/9/26 20:07:38

Docker边缘计算实战部署方案(边缘场景优化全解析)

第一章&#xff1a;Docker边缘计算部署概述随着物联网和5G技术的快速发展&#xff0c;边缘计算已成为现代分布式系统架构中的关键组成部分。在资源受限、网络不稳定或延迟敏感的边缘环境中&#xff0c;传统应用部署方式难以满足实时性和可维护性的需求。Docker凭借其轻量级容器…

作者头像 李华
网站建设 2026/9/26 20:06:52

Docker日志轮转终极指南(从小白到专家的4种实战方案)

第一章&#xff1a;Docker日志轮转的核心挑战与重要性在容器化部署日益普及的今天&#xff0c;Docker日志管理成为保障系统稳定运行的关键环节。默认情况下&#xff0c;Docker使用json-file日志驱动记录容器输出&#xff0c;若不加以控制&#xff0c;日志文件将持续增长&#x…

作者头像 李华