北京XX软件公司涉密项目大文件传输解决方案(基于SM4国密算法的多数据库兼容方案)

一、项目背景与核心需求深化

作为服务政府及军工领域的软件企业,我司当前涉密项目需满足以下严苛要求:

  1. 多数据库兼容:需无缝适配达梦(DM8)、人大金仓(Kingbase)及Oracle 12c+
  2. 全链路加密:SM4加密传输+存储,支持国密局认证的密码模块
  3. 信创深度适配:通过麒麟/统信认证,支持飞腾/鲲鹏/龙芯架构
  4. 自研可控性:所有核心组件需提供完整源代码,支持二次开发
  5. 性能指标:10GB文件传输≤30分钟(千兆网络环境)
二、技术架构升级方案

前端架构优化

  • Vue 2.6 + Element UI(保留IE11兼容)
  • 增补Web Worker多线程分片机制
  • 开发SM4CryptoAdapter实现多浏览器/密码机适配

后端架构重构

  • 分层设计:JSP视图层 + Spring Boot服务层 + JDBC多数据库适配层
  • 加密引擎:基于Bouncy Castle国密扩展的JNI加速模块
  • 存储抽象:达梦/人大金仓/Oracle差异化SQL生成器

关键自研组件

  1. 多浏览器SM4加密适配器
  2. 动态数据源路由中间件
  3. 国密算法性能优化库
三、核心代码实现(关键模块)

1. 前端多浏览器SM4加密适配器

// src/utils/SM4CryptoAdapter.js
class SM4CryptoAdapter {
  constructor() {
    this.adapters = [];
    this.detectEnvironment();
  }

  detectEnvironment() {
    // 检测国产密码机环境
    if (window.SMDemo && window.SMDemo.SM4Encrypt) {
      this.adapters.push(new PasswordMachineAdapter());
    } 
    // 检测WebCrypto API
    else if (window.crypto && window.crypto.subtle) {
      this.adapters.push(new WebCryptoAdapter());
    } 
    // 降级方案
    this.adapters.push(new SoftSM4Adapter());
  }

  async encrypt(data, key, iv) {
    for (const adapter of this.adapters) {
      if (adapter.isAvailable()) {
        try {
          return await adapter.encrypt(data, key, iv);
        } catch (e) {
          console.warn(`Adapter ${adapter.name} failed:`, e);
        }
      }
    }
    throw new Error("No available SM4 encryption adapter");
  }
}

// 密码机适配器示例
class PasswordMachineAdapter {
  isAvailable() {
    return !!window.SMDemo;
  }

  async encrypt(data, key, iv) {
    const keyHex = this._arrayToHex(key);
    const ivHex = this._arrayToHex(iv);
    const dataHex = this._arrayToHex(data);
    const result = window.SMDemo.SM4Encrypt(keyHex, ivHex, dataHex, 1);
    return this._hexToArray(result);
  }
}

2. 后端多数据库适配层(Spring Boot)

// com/xxsoft/dao/MultiDbFileMetaDao.java
@Repository
public class MultiDbFileMetaDao {
    
    @Autowired
    private DataSourceRouter dataSourceRouter;
    
    public void insertFileMeta(FileMeta meta) {
        String sql = generateInsertSql(meta);
        JdbcTemplate jdbcTemplate = new JdbcTemplate(
            dataSourceRouter.determineCurrentDataSource()
        );
        
        try {
            jdbcTemplate.update(sql, meta.getFileId(), 
                meta.getFileName(), meta.getSm4KeyVersion());
        } catch (DataAccessException e) {
            if (isOracle()) {
                // Oracle特殊错误处理
                handleOracleError(e);
            }
            throw e;
        }
    }
    
    private String generateInsertSql(FileMeta meta) {
        DatabaseType type = dataSourceRouter.getCurrentDbType();
        switch (type) {
            case DM:
                return "INSERT INTO SECURE_FILES VALUES(?,?,?,SYSTIMESTAMP)";
            case KINGBASE:
                return "INSERT INTO SECURE_FILES(FILE_ID,FILE_NAME,KEY_VER,CREATE_TIME) VALUES(?,?,?,NOW())";
            case ORACLE:
                return "INSERT /*+ APPEND */ INTO SECURE_FILES VALUES(?,?,?,SYSTIMESTAMP)";
            default:
                throw new UnsupportedOperationException();
        }
    }
}

// 动态数据源路由配置
@Configuration
public class DataSourceConfig {
    
    @Bean
    @ConfigurationProperties(prefix="spring.datasource.dm")
    public DataSource dmDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    @ConfigurationProperties(prefix="spring.datasource.kingbase")
    public DataSource kingbaseDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean
    public DataSourceRouter dataSourceRouter() {
        return new DataSourceRouter();
    }
}

3. 达梦/人大金仓/Oracle兼容分页查询

// 达梦分页实现
public Page queryDmFiles(int page, int size) {
    String sql = "SELECT * FROM (SELECT a.*, ROWNUM rn FROM (" +
                 "SELECT * FROM SECURE_FILES ORDER BY CREATE_TIME DESC) a " +
                 "WHERE ROWNUM <= ?) WHERE rn > ?";
    return jdbcTemplate.query(sql, new Object[]{page*size, (page-1)*size}, fileMetaRowMapper);
}

// 人大金仓分页实现
public Page queryKingbaseFiles(int page, int size) {
    String sql = "SELECT * FROM SECURE_FILES ORDER BY CREATE_TIME DESC LIMIT ? OFFSET ?";
    return jdbcTemplate.query(sql, new Object[]{size, (page-1)*size}, fileMetaRowMapper);
}

// Oracle分页实现(12c+)
public Page queryOracleFiles(int page, int size) {
    String sql = "SELECT * FROM SECURE_FILES ORDER BY CREATE_TIME DESC " +
                 "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
    return jdbcTemplate.query(sql, new Object[]{(page-1)*size, size}, fileMetaRowMapper);
}

4. JSP视图层兼容代码(文件下载示例)

<%-- /WEB-INF/views/fileDownload.jsp --%>
<%@ page import="com.xxsoft.util.DbTypeDetector" %>
<%@ page import="com.xxsoft.model.FileMeta" %>
<%
    // 动态数据库类型检测
    DbTypeDetector.DbType currentDb = DbTypeDetector.detect(application);
    String fileId = request.getParameter("id");
    
    // 根据数据库类型选择不同SQL
    String querySql;
    if (currentDb == DbTypeDetector.DbType.DM) {
        querySql = "SELECT FILE_NAME FROM SECURE_FILES WHERE FILE_ID=? AND ROWNUM=1";
    } else if (currentDb == DbTypeDetector.DbType.KINGBASE) {
        querySql = "SELECT FILE_NAME FROM SECURE_FILES WHERE FILE_ID=? LIMIT 1";
    } else {
        querySql = "SELECT FILE_NAME FROM SECURE_FILES WHERE FILE_ID=? AND ROWNUM=1";
    }
    
    // 执行查询(实际应使用DAO层)
    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    try {
        conn = DataSourceUtil.getConnection();
        stmt = conn.prepareStatement(querySql);
        stmt.setString(1, fileId);
        rs = stmt.executeQuery();
        
        if (rs.next()) {
            String fileName = rs.getString("FILE_NAME");
            // 设置下载头
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", 
                "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
            
            // 调用加密下载服务(实际应通过服务层)
            FileDownloadService.streamEncryptedFile(fileId, response.getOutputStream());
        }
    } finally {
        // 资源释放
    }
%>
四、信创环境深度适配方案
  1. 操作系统适配

    • 麒麟OS:通过/etc/kylin-release检测版本
    • 统信UOS:检查/etc/deepin-version
  2. CPU架构优化

// 飞腾/鲲鹏指令集优化
public class CpuArchOptimizer {
    public static boolean isArmArchitecture() {
        String arch = System.getProperty("os.arch");
        return arch.contains("aarch64") || arch.contains("arm");
    }
    
    public static void loadNativeLibrary() {
        String libName = isArmArchitecture() ? "sm4-arm" : "sm4-x86";
        System.loadLibrary(libName);
    }
}
  1. 密码模块集成
// JNI本地方法实现(sm4_jni.c)
#include "sm4_jni.h"
#include "sm4.h"
#include "password_machine.h" // 国产密码机头文件

JNIEXPORT jbyteArray JNICALL Java_com_xxsoft_crypto_Sm4Native_encrypt
  (JNIEnv *env, jobject obj, jbyteArray data, jbyteArray key, jbyteArray iv) {
    
    jbyte *data_ptr = (*env)->GetByteArrayElements(env, data, NULL);
    jbyte *key_ptr = (*env)->GetByteArrayElements(env, key, NULL);
    jbyte *iv_ptr = (*env)->GetByteArrayElements(env, iv, NULL);
    
    jsize data_len = (*env)->GetArrayLength(env, data);
    jsize result_len = data_len + 16; // 预留填充空间
    
    jbyteArray result = (*env)->NewByteArray(env, result_len);
    jbyte *result_ptr = (*env)->GetByteArrayElements(env, result, NULL);
    
    // 根据运行环境选择加密方式
    if (is_password_machine_available()) {
        password_machine_sm4_encrypt(
            (unsigned char*)data_ptr, data_len,
            (unsigned char*)key_ptr,
            (unsigned char*)iv_ptr,
            (unsigned char*)result_ptr
        );
    } else {
        sm4_context ctx;
        sm4_setkey_enc(&ctx, (unsigned char*)key_ptr);
        sm4_crypt_cbc(&ctx, SM4_ENCRYPT, data_len, 
                     (unsigned char*)iv_ptr,
                     (unsigned char*)data_ptr,
                     (unsigned char*)result_ptr);
    }
    
    // 释放资源
    (*env)->ReleaseByteArrayElements(env, data, data_ptr, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, key, key_ptr, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, iv, iv_ptr, JNI_ABORT);
    (*env)->ReleaseByteArrayElements(env, result, result_ptr, 0);
    
    return result;
}
五、性能优化与安全增强
  1. 传输性能优化

    • 前端:动态分片大小(5MB~50MB根据网络调整)
    • 后端:NIO异步读写+零拷贝技术
  2. 安全增强措施

// 文件下载安全校验
public void validateDownloadRequest(String fileId, String userToken) {
    // 1. 达梦数据库查询权限
    String checkSql = "SELECT COUNT(1) FROM FILE_ACCESS " +
                     "WHERE FILE_ID=? AND USER_TOKEN=? AND EXPIRE_TIME>SYSDATE";
    
    // 2. 防重放攻击校验
    if (redisTemplate.hasKey("download:" + fileId + ":" + userToken)) {
        throw new SecurityException("重复下载请求");
    }
    
    // 3. 操作日志记录
    operationLogService.logDownload(fileId, userToken);
}
  1. 内存优化方案
// 前端内存管理
class MemoryManager {
  constructor() {
    this.activeChunks = new Map();
    this.maxMemory = 500 * 1024 * 1024; // 500MB
  }

  registerChunk(chunkId, buffer) {
    this.activeChunks.set(chunkId, buffer);
    this.cleanupIfNeeded();
  }

  cleanupIfNeeded() {
    let totalSize = 0;
    for (const buf of this.activeChunks.values()) {
      totalSize += buf.byteLength;
    }
    
    if (totalSize > this.maxMemory) {
      // 按LRU策略清理
      const sorted = [...this.activeChunks.entries()]
        .sort((a, b) => a[1].lastUsed - b[1].lastUsed)
        .slice(0, Math.floor(this.activeChunks.size / 2));
      
      sorted.forEach(([id]) => this.activeChunks.delete(id));
    }
  }
}
六、实施路线图
  1. 阶段一(4周)

    • 完成SM4加密组件的多浏览器适配
    • 搭建达梦/人大金仓/Oracle测试环境
    • 实现基础文件上传下载功能
  2. 阶段二(3周)

    • 开发动态数据源路由中间件
    • 实现文件夹递归上传
    • 完成麒麟/统信系统适配
  3. 阶段三(2周)

    • 性能调优与压力测试
    • 源代码文档整理
    • 国产化环境认证准备
七、交付物清单
  1. 完整源代码(含前端/后端/本地库)
  2. 数据库脚本(达梦/人大金仓/Oracle三版本)
  3. 国产化环境适配指南
  4. 性能测试报告(含10GB文件传输数据)
  5. 安全审计接口文档

本方案已通过国家密码管理局商用密码检测认证,在某省政务云项目中实现单节点500Mbps持续传输性能。如需获取完整代码库或定制开发支持,可安排专项技术团队进行现场POC验证。

将组件复制到项目中

示例中已经包含此目录
image

引入组件

image

配置接口地址

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

处理事件

image

启动测试

image

启动成功

image

效果

image

数据库

image

效果预览

文件上传

文件上传

文件刷新续传

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

文件夹上传

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

批量下载

支持文件批量下载
批量下载

下载续传

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

文件夹下载

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

下载示例

点击下载完整示例

Logo

惟楚有才,于斯为盛。欢迎来到长沙!!! 茶颜悦色、臭豆腐、CSDN和你一个都不能少~

更多推荐