MongoDB如何字段级加密储存和查询

MongoDB4.2企业版引入了字段级别加密 详情

MongoDB的手册说字段级加密的自动功能仅在MongoDB 4.2 Enterprise和MongoDB Atlas 4.2集群中可用。详情

既然不能使用4.2的字段级别加密,那就换种方式,引入插件!mongoose-field-encryption

环境要求:

  • Node >=6 (Use 2.3.4 for Node >=4.4.7 && <=6.x.x)
  • MongoDB >=2.6.10
  • Mongoose >=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({}) // 查询时结果就是解密的

猜你喜欢

转载自blog.csdn.net/sinat_15955423/article/details/107026985