news 2026/4/16 18:22:00

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/4/16 13:32:22

VictoriaMetrics指标存储:远程写入配置AI生成示例

VictoriaMetrics指标存储&#xff1a;远程写入配置AI生成示例 在现代云原生架构中&#xff0c;监控系统早已不再是“能看就行”的辅助工具&#xff0c;而是保障服务稳定、驱动性能优化的核心能力。Prometheus 作为这一领域的事实标准&#xff0c;凭借其强大的多维数据模型和灵活…

作者头像 李华
网站建设 2026/4/16 13:31:51

JuiceFS云原生存储:元数据引擎+对象存储绑定AI指导

JuiceFS云原生存储&#xff1a;元数据引擎对象存储绑定AI指导 在AI模型日益轻量化、边缘化部署的今天&#xff0c;一个15亿参数的语言模型如何做到“开机即用”&#xff1f;更进一步&#xff0c;当多个用户同时访问同一模型服务时&#xff0c;系统又该如何保障性能不降、数据一…

作者头像 李华
网站建设 2026/4/16 13:33:08

阿里云OSS工具使用:断点续传+生命周期规则AI设置

阿里云OSS工具使用&#xff1a;断点续传与生命周期规则的智能协同 在AI模型研发进入“快迭代、轻部署”时代的今天&#xff0c;一个常被忽视却至关重要的问题浮出水面&#xff1a;如何高效、可靠地将动辄数GB的模型镜像稳定上传到云端&#xff0c;并在后续自动完成存储优化&…

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

Vultr Block Storage附加:挂载+格式化+开机自动挂载脚本

Vultr Block Storage附加&#xff1a;挂载格式化开机自动挂载脚本 在部署轻量级AI模型如VibeThinker-1.5B-APP的实践中&#xff0c;一个常见的瓶颈并非算力不足&#xff0c;而是系统盘空间迅速耗尽。这类模型虽参数规模不大&#xff0c;但在推理过程中会产生大量缓存文件、用户…

作者头像 李华
网站建设 2026/4/16 15:17:59

容器日志失控导致服务崩溃?你必须掌握的日志轮转3大机制

第一章&#xff1a;容器日志失控导致服务崩溃&#xff1f;一个被忽视的运维黑洞在现代微服务架构中&#xff0c;容器化部署已成为标准实践&#xff0c;但伴随而来的日志管理问题却常常被低估。当日志未被合理轮转或限制时&#xff0c;单个容器可能在数小时内生成数十GB的日志文…

作者头像 李华
网站建设 2026/4/16 2:14:26

为什么90%的团队都忽略了Docker标签治理?揭开自动化运维盲区

第一章&#xff1a;Docker镜像标签管理的重要性Docker 镜像标签&#xff08;Tag&#xff09;是识别和管理容器镜像版本的关键机制。一个镜像可以拥有多个标签&#xff0c;用于表示不同的发布状态&#xff0c;例如开发、测试或生产环境的版本。合理的标签策略能够提升部署的可追…

作者头像 李华