从零构建遗嘱数字档案馆系统完整代码与部署
系统架构与环境准备
本系统采用Node.js作为后端运行环境,MongoDB作为数据存储引擎,利用AES-256-GCM算法对遗嘱内容进行高强度加密存储。系统核心包含数据加密层、持久化层和API交互层。在开始编码前,必须确保开发环境已安装以下基础组件。
1. 安装Node.js
请确保安装了Node.js v16.0及以上版本。如果尚未安装,请使用NVM(Node Version Manager)进行安装,命令如下:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install --lts
2. 安装并启动MongoDB
为了保证环境一致性,推荐使用Docker容器快速启动MongoDB服务。执行以下命令拉取镜像并启动容器,映射端口至本地27017:
docker run -d -p 27017:27017 --name will-mongo -e MONGO_INITDB_ROOT_USERNAME=admin -e MONGO_INITDB_ROOT_PASSWORD=secret123 mongo:latest
执行完毕后,使用docker ps确认容器运行正常。
项目初始化与依赖安装
在本地创建项目目录并初始化Node.js项目。我们将创建一个名为will-archive-system的文件夹,并配置必要的依赖包。
1. 创建项目目录
mkdir will-archive-system && cd will-archive-system
npm init -y
2. 安装核心依赖
执行以下命令安装Express框架、MongoDB驱动(Mongoose)、加密库及跨域处理中间件:
npm install express mongoose cors body-parser dotenv crypto
其中crypto为Node.js内置模块,无需安装,但为了代码提示清晰,建议在TypeScript项目中引入类型定义,本指南使用原生JavaScript。
3. 配置环境变量
在项目根目录下创建.env文件,用于存放数据库连接字符串和加密密钥盐值。内容如下:
PORT=3000
MONGO_URI=mongodb://admin:secret123@localhost:27017/will_db?authSource=admin
ENCRYPTION_KEY=your-super-secret-32-byte-key-string
数据模型与数据库连接
我们需要定义一个严格的数据模型来存储遗嘱信息。为了安全起见,数据库中仅存储加密后的密文、初始化向量(IV)以及用于完整性校验的哈希值,绝不存储明文。
1. 创建数据模型文件

在根目录下创建models文件夹,并新建Will.js文件:
const mongoose = require('mongoose');
const WillSchema = new mongoose.Schema({
testatorName: { type: String, required: true }, // 立遗嘱人姓名
encryptedContent: { type: String, required: true }, // AES加密后的密文
iv: { type: String, required: true }, // 初始化向量
authHash: { type: String, required: true }, // 内容哈希,用于验证解密后完整性
createdAt: { type: Date, default: Date.now },
status: { type: String, default: 'ACTIVE', enum: ['ACTIVE', 'REVOKED'] }
});
module.exports = mongoose.model('Will', WillSchema);
2. 配置数据库连接
在根目录下创建db.js,编写数据库连接逻辑:
const mongoose = require('mongoose');
require('dotenv').config();
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI);
console.log('MongoDB Connected Successfully');
} catch (err) {
console.error('Database Connection Error:', err.message);
process.exit(1);
}
};
module.exports = connectDB;
核心加密解密工具类开发
这是系统的安全核心。我们将创建一个工具类封装AES-256-GCM的加密与解密逻辑。AES-256-GCM模式不仅提供保密性,还提供数据完整性校验。
在根目录下创建utils文件夹,新建crypto.js:
const crypto = require('crypto');
const algorithm = 'aes-256-gcm';
const keyLength = 32;
const saltLength = 16;
const ivLength = 16;
const tagLength = 16;
// 密钥派生函数:使用PBKDF2从环境变量密钥生成固定长度的Key
function getKeyFromPassword(password, salt) {
return crypto.pbkdf2Sync(password, salt, 100000, keyLength, 'sha256');
}
// 加密函数
exports.encrypt = (text) => {
const iv = crypto.randomBytes(ivLength);
const salt = crypto.randomBytes(saltLength);
const key = getKeyFromPassword(process.env.ENCRYPTION_KEY, salt);
const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag(); // 获取认证标签
// 返回格式:salt + iv + authTag + encrypted
return {
content: encrypted,
iv: iv.toString('hex'),
salt: salt.toString('hex'),
authTag: authTag.toString('hex')
};
};
// 解密函数
exports.decrypt = (encryptedContent, ivHex, saltHex, authTagHex) => {
const salt = Buffer.from(saltHex, 'hex');
const iv = Buffer.from(ivHex, 'hex');
const authTag = Buffer.from(authTagHex, 'hex');
const key = getKeyFromPassword(process.env.ENCRYPTION_KEY, salt);
const decipher = crypto.createDecipheriv(algorithm, key, iv);
decipher.setAuthTag(authTag);
let decrypted = decipher.update(encryptedContent, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
};
后端业务逻辑与API接口实现
编写主服务器文件server.js,整合数据库连接、加密逻辑和Express路由。我们将提供三个核心接口:创建遗嘱、获取遗嘱列表、解密并查看特定遗嘱。
const express = require('express'); const cors = require('cors'); const bodyParser = require('body-parser'); require('dotenv').config(); const connectDB = require('./db'); const Will = require('./models/Will'); const { encrypt, decrypt } = require('./utils/crypto'); const app = express(); // 中间件配置 app.use(cors()); app.use(bodyParser.json({ limit: '10mb' })); // 允许较大的遗嘱文本 // 1. 创建遗嘱接口 app.post('/api/wills', async (req, res) => { try { const { testatorName, content } = req.body; if (!content || !testatorName) { return res.status(400).json({ error: 'Missing required fields' }); } // 生成内容哈希用于后续验证 const contentHash = crypto.createHash('sha256').update(content).digest('hex'); // 加密内容 const encryptedData = encrypt(content); const newWill = new Will({ testatorName, encryptedContent: encryptedData.content, iv: encryptedData.iv, salt: encryptedData.salt, // 需要在Model中添加salt字段,这里为了演示逻辑 authTag: encryptedData.authTag, // 需要在Model中添加authTag字段 authHash: contentHash }); // 注意:请确保models/Will.js中包含 salt 和 authTag 字段 // const WillSchema = new mongoose.Schema({ ..., salt: String, authTag: String ... }); await newWill.save(); res.status(201).json({ message: 'Will archived successfully', id: newWill._id }); } catch (err) { res.status(500).json({ error: err.message }); } }); // 2. 获取遗嘱列表(仅返回元数据,不返回内容) app.get('/api/wills', async (req, res) => { try { const wills = await Will.find({}, 'testatorName createdAt status'); res.json(wills); } catch (err) { res.status(500).json({ error: err.message }); } }); // 3. 查看并解密遗嘱 app.get('/api/wills/:id', async (req, res) => { try { const will = await Will.findById(req.params.id); if (!will) return res.status(404).json({ error: 'Will not found' }); // 解密操作 const decryptedContent = decrypt( will.encryptedContent, will.iv, will.salt, will.authTag ); // 验证完整性 const currentHash = crypto.createHash('sha256').update(decryptedContent).digest('hex'); if (currentHash !== will.authHash) { return res.status(500).json({ error: 'Data integrity check failed' }); } res.json({ testatorName: will.testatorName, content: decryptedContent, createdAt: will.createdAt }); } catch (err) { res.status(500).json({ error: err.message }); } }); // 启动服务 const PORT = process.env.PORT || 3000; const startServer = async () => { await connectDB(); app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); }); }; startServer();重要修正:请在
models/Will.js中添加salt和authTag字段,否则上述代码会报错。完整的Schema应包含这两个字段。前端交互页面构建
为了方便测试,我们在
public目录下创建一个简单的HTML页面,通过Fetch API与后端交互。创建
public/index.html:遗嘱数字档案馆系统 遗嘱数字档案馆系统1. 归档新遗嘱
2. 读取遗嘱
修改
server.js,添加静态文件托管支持,以便访问该页面:app.use(express.static('public'));系统运行与功能测试
所有代码已准备就绪,现在启动系统并进行全流程测试。
1. 启动服务
在项目根目录下执行:
node server.js控制台应输出“MongoDB Connected Successfully”和“Server running on port 3000”。
2. 归档测试
- 打开浏览器访问
http://localhost:3000。- 在“归档新遗嘱”区域,输入姓名和一段敏感的遗嘱文本。
- 点击“加密并归档”按钮。
- 观察返回的JSON结果,记录下返回的
_id(例如:65f1a2b3c4d5e6f7g8h9i0j1)。3. 存储验证
打开MongoDB Compass或使用命令行连接数据库,查看
will_db数据库中的wills集合。你会发现encryptedContent字段是一串乱码,证明明文未直接存储。4. 解密测试
- 将步骤2中获取的ID填入“读取遗嘱”输入框。
- 点击“解密并查看”。
- 页面下方将显示解密后的原始遗嘱内容和立遗嘱人姓名,证明加密解密流程闭环成功。
5. 完整性破坏测试(进阶)
为了验证安全性,可以直接在数据库中修改某条记录的
encryptedContent的一个字符,然后再次执行读取操作。系统应返回“Data integrity check failed”或抛出解密错误,这是因为AES-GCM的AuthTag校验失败,确保了数据不可篡改。