如何在MongoDB中为存储的OTP等特定数据设置1分钟过期?
在Mongoose用户模型中实现OTP自动过期的方案
当然可以实现OTP的自动过期,不过需要注意MongoDB的TTL索引特性限制,下面针对你的代码问题给出两种可行方案:
先修正当前代码的两个问题
createdAt默认值用Date()会生成字符串而非Date对象,需改为Date.now或函数式生成- 模块级的
currentTime只会在服务启动时计算一次,无法动态生成每个OTP的过期时间,需改为动态计算
方案1:使用独立OTP集合(推荐)
MongoDB的TTL索引只能针对整个文档生效,无法单独删除数组中的子文档。因此最合理的方式是将OTP单独存储为一个集合,利用TTL索引自动删除过期记录,不影响用户主文档。
const mongoose = require("mongoose"); // 单独的OTP模型 const otpSchema = new mongoose.Schema({ userId: { type: mongoose.Schema.Types.ObjectId, ref: "user", required: true }, otp: { type: String, required: true }, createdAt: { type: Date, default: Date.now, index: { expires: '1m' } // 配置1分钟后自动过期 } }); const OtpModel = mongoose.model("Otp", otpSchema); module.exports = OtpModel;
使用方式:
- 生成OTP时,创建一条Otp文档关联对应用户
- 验证OTP时,查询是否存在未过期的记录
- 验证成功后可手动删除该OTP记录
方案2:在用户模型中手动清理过期OTP
如果必须将OTP存在用户文档的数组中,需要手动清理过期的子文档:
第一步:修正用户模型的日期字段
const mongoose = require("mongoose"); const userSchema = new mongoose.Schema( { fullName: { type: String, required: false }, email: { type: String, required: false }, mobile_number: { type: String, required: false }, password: { type: String, required: false }, otp_instance: [{ otp_id: { type: mongoose.Schema.Types.ObjectId, required: false }, otp: { type: String, required: false }, createdAt: { type: Date, default: Date.now // 生成Date类型的当前时间 }, expiration_time: { type: Date, default: () => new Date(Date.now() + 60 * 1000) // 动态计算1分钟后时间 } }], resetLink: { type: String, required: false }, isAccountVerified: { type: String, required: false }, token: { type: String, required: false }, activeStatus: { type: String, required: false, default: "0" }, deletedStatus: { type: String, required: false, default: "0" }, }, { timestamps: true } ); // 添加清理过期OTP的实例方法 userSchema.methods.cleanExpiredOtp = async function() { await this.updateOne({ $pull: { otp_instance: { expiration_time: { $lt: new Date() } } } }); }; const userModel = mongoose.model("user", userSchema); module.exports = userModel;
第二步:使用时清理过期OTP
每次添加新OTP前,先清理用户的过期记录:
// 示例:给用户添加新OTP const user = await userModel.findById(userId); await user.cleanExpiredOtp(); user.otp_instance.push({ otp: "123456" }); await user.save();
可选:定时全局清理
如果需要批量清理所有用户的过期OTP,可添加定时任务:
// 每分钟执行一次清理 setInterval(async () => { await userModel.updateMany( {}, { $pull: { otp_instance: { expiration_time: { $lt: new Date() } } } } ); }, 60 * 1000);
内容的提问来源于stack exchange,提问作者sAcHiN pAtEl
相关产品推荐
相关产品推荐

