MongoDB4.2企业版引入了字段级别加密 详情
MongoDB的手册说字段级加密的自动功能仅在MongoDB 4.2 Enterprise和MongoDB Atlas 4.2集群中可用。详情
既然不能使用4.2的字段级别加密,那就换种方式,引入插件!mongoose-field-encryption
环境要求:
>=6
(Use 2.3.4
for Node >=4.4.7 && <=6.x.x
)>=2.6.10
>=4.0.0
以egg.js为例
npm install mongoose-field-encryption --save-exact
app/model/message.js
module.exports = app => {
const mongoose = app.mongoose
const Schema = mongoose.Schema;
const mongooseFieldEncryption = require("mongoose-field-encryption").fieldEncryption;
const messageSchema = new Schema({
title: String,
message: String,
phone: String
});
messageSchema.plugin(mongooseFieldEncryption, {
fields: ["message", "phone"],
secret: "liubao",
saltGenerator: function (secret) {
return "1234567890123456"; //理想情况下,应使用该机密返回长度为16的字符串
}
});
return mongoose.model('message', messageSchema)
}
app/model/post.js
module.exports = app => {
const mongoose = app.mongoose
const mongooseFieldEncryption = require("mongoose-field-encryption").fieldEncryption;
const Schema = mongoose.Schema;
const PostSchema = new Schema({
title: String,
message: String,
references: {
author: String,
date: Date
}
});
PostSchema.plugin(mongooseFieldEncryption, {fields: ["message", "references"], secret: "liubao"})
return mongoose.model('post', PostSchema)
}
app/controller/test.js
const Controller = require('egg').Controller;
class TestController extends Controller {
async test() {
const {ctx} = this;
const messageModel = this.app.mongoose.model('message')
const postModel = this.app.mongoose.model('post')
const post = new postModel({title: "some text", message: "hello all"});
post.save(function (err) {
console.log(post.title); //一些文本(仅通过选项将消息字段设置为加密)
console.log(post.message); // a9ad74603a91a2e97a803a367ab4e04d:93c64bf4c279d282deeaf738fabebe89
console.log(post.__enc_message); // true
});
const title = "测试标题";
const phone = "15900000000";
const message = "hello all";
const messageToSave = new messageModel({title, message, phone});
await messageToSave.save();
// note that we are only providing the field we would like to search with
const messageToSearchWith = new messageModel({phone});
messageToSearchWith.encryptFieldsSync();
// `messageToSearchWith.name` contains the encrypted string text
const results = await messageModel.find({phone: messageToSearchWith.phone});
ctx.body = results
}
}
module.exports = TestController;
模拟请求:
结果:
结果成功!
非egg.js框架的话可以参考如下自行修改,效果一样
const Controller = require('egg').Controller;
const mongoose = require("mongoose");
const mongooseFieldEncryption = require("mongoose-field-encryption").fieldEncryption;
const Schema = mongoose.Schema;
const PostSchema = new Schema({
title: String,
message: String,
references: {
author: String,
date: Date
}
});
PostSchema.plugin(mongooseFieldEncryption, {fields: ["message", "references"], secret: "liubao"})
const messageSchema = new Schema({
title: String,
message: String,
name: String
});
messageSchema.plugin(mongooseFieldEncryption, {
fields: ["message", "phone"],
secret: "liubao",
saltGenerator: function (secret) {
return "1234567890123456"; //理想情况下,应使用该机密返回长度为16的字符串
}
});
class TestController extends Controller {
async test() {
const { ctx } = this;
const Post = mongoose.model("Post", PostSchema);
const post = new Post({ title: "some text", message: "hello all" });
post.save(function(err) {
console.log(post.title); //一些文本(仅通过选项将消息字段设置为加密)
console.log(post.message);
console.log(post.__enc_message); // true
});
const title = "some text";
const phone = "15900000000";
const message = "hello all";
const Message = mongoose.model("Message", messageSchema);
const messageToSave = new Message({title, message, phone});
await messageToSave.save();
// note that we are only providing the field we would like to search with
const messageToSearchWith = new Message({phone});
messageToSearchWith.encryptFieldsSync();
// `messageToSearchWith.name` contains the encrypted string text
const results = await Message.find({phone: messageToSearchWith.phone});
ctx.body = results
}
}
module.exports = TestController;
mongoose-field-encryption加密子文档(嵌套文档)
可以在定义加密字段时这样定义:
const mongoose = require("mongoose");
const schema = new mongoose.Schema({
fieldA: { // 字段A
type: String
},
fieldBArrays: [new mongoose.Schema({ // 字段B嵌套文档
child: { // 子字段
type: String
}
})]
});
const mongooseFieldEncryption = require('mongoose-field-encryption').fieldEncryption;
schema.plugin(mongooseFieldEncryption, {
fields: ["fieldA","fieldBArrays.$.child"],
secret: "秘钥",
saltGenerator: function (secret) {
return "1234567890123456"; //理想情况下,应使用该机密返回长度为16的字符串
}
});
上面这个例子更新时加密。但是查询时切记不能过滤字段,否则就返回的是加密的数据
也可以用下面的方法巧妙的使用子文档加解密
const mongoose = require('mongoose');
const mongooseFieldEncryption = require("mongoose-field-encryption").fieldEncryption;
const CredentialSchema = new mongoose.Schema({
type: {
required: true,
type: String,
},
value: {
required: true,
type: String,
},
});
CredentialSchema.plugin(mongooseFieldEncryption, {
fields: ["value"],
secret: process.env.MONGOOSE_ENCRYPTION_KEY,
});
const accountSchema = new mongoose.Schema({
provider: {
type: String,
required: true,
lowercase: true,
trim: true,
},
credentials: [CredentialSchema],
owner: {
required: true,
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
});
exports.xxModel = mongoose.model('Account', accountSchema);
await new xxModel(data).save() // 保存
await xxModel.find({}) // 查询时结果就是解密的