SetTimeout 在 Mongoose 模式后中间件中不起作用

Posted

技术标签:

【中文标题】SetTimeout 在 Mongoose 模式后中间件中不起作用【英文标题】:SetTimeout is not working in Mongoose schema post middleware 【发布时间】:2019-08-19 00:29:53 【问题描述】:

在生成forgetpassword_token 24 小时后,我正在尝试将 mongodb 文档中的forgetpassword_token 更新为null。所以我使用的是 Mongoose 模式中间件和 setTimeout,但 setTimeout 不起作用。

我已尝试实现异步等待,但根据我的结果也无法正常工作。

CompanySchema.post('updateOne',true, function(doc,next)
    next();
       setTimeout(this.update(, $set:  forgetpassword_token: null  ).then(result=>
           console.log(result);
                   ),10000000000);
       );

【问题讨论】:

setTimeout 将函数作为参数。但是,您正在向它传递一个 Promise。 【参考方案1】:

这里的主要问题是这个实现是有缺陷的,因为如果您的节点应用程序在 24 小时窗口内重新启动,您的超时将消失(是内存中的对象,没有持久化)并且令牌将保持活动状态,使您面临安全风险。

手动令牌验证

一个非常常见的解决方案是将token_expiration_date 与令牌一起保存,在相关的密码重置请求期间进行日期比较。如果token_expiration_date过期,请求返回错误,服务器必须删除db上的token。

您也可以相反:将token_creation_datemax-token-ttl 存储在您的应用代码中(例如24 小时)。在任何情况下,您都会在请求时进行日期比较。

@NikKyriakides 建议(参见 cmets)这种方法的更复杂的版本:您创建一个 JWT 令牌 contains itself the expiration date。当用户请求重置密码页面时,如果令牌有效,则只需 verify 调用单个方法(无需手动日期比较)。

Mongo 过期选项

更优雅和有效的解决方案是为您的忘记密码_令牌创建一个不同的 mongoose 架构,并使用本机 mongo/mongoose expire option 在创建后的固定时间后自动删除文档。

const secondsInADay = 60 * 60 * 24;

const tokenSchema = mongoose.Schema(
    value: String
, timestamps: true);

tokenSchema.index(createdAt: 1,expireAfterSeconds: secondsInADay);
const Token = mongoose.model('Token', tokenSchema);

然后向您现有的 CompanySchema 添加对此模式的引用:

forgetpassword_token: type: mongoose.Schema.Types.ObjectId, ref: 'Token'

this topic 上存在很多问题,所以也请与相关的mongoose documentation 一起检查它们。

作业调度程序

另一种方法是使用像agenda 这样的作业调度程序每小时检查过期令牌并删除它们。是的,您可以编写一个基于setTimeout 的检查作为您的应用程序的模块,但是如果还存在合适的工具,为什么不使用它呢?另请查看下面的@NikKyriakides cmets,了解此解决方案的潜在缺点。

【讨论】:

你不能在令牌本身中对创建日期时间进行编码/加密,然后解密它并根据当前时间检查它是否过期? IRRC JWT 做了类似的事情。 是的@NikKyriakides,这是另一种可能的方法。但是当涉及到安全性时,我宁愿尽可能少写代码/逻辑,因为在编写编码/比较过程时,我们可能会添加有风险的错误。在项目的生命周期中,代码可能会被无意更改。此外,将特定组件(mongo 本身或议程)委派给主动查找过期令牌,我们可以确保它们被尽快删除。安全性也与微小的细节有关。显然这是我的 POV。 我认为您的建议违反了KISS。如果您的作业调度程序出现故障怎么办?如果作业调度程序运行时数据库有锁怎么办?这是否意味着过期的令牌不会被删除?如果我在令牌过期后但在作业调度程序运行之前尝试执行忘记密码怎么办?代码也可能会因您的提议而被无意更改。有人将您的作业调度程序修改为简单地.. 不运行。被动措施总是比主动措施更好。 还有when security is involved I'll prefer to write less code/logic as possible...。与简单地编写一个解码 JWT 并将日期时间与当前时间进行比较的中间件相比,如何涉及和配置一个需要始终开启的作业调度程序?我想我不需要说我不是在攻击你,而是你提出的解决方案的逻辑。 @NikKyriakides 我的声明主要提到了 mongo expire 选项。是的,调度程序解决方案可能会过度杀伤并且有其缺点,但在某些情况下可能需要它(例如,通知用户这或令牌管理中需要更多逻辑时)。我过去多次使用基于日期比较的解决方案,现在我将根据您的建议使用另一种解决方案更新答案。也感谢您的批评:当它基于 fatcs 时,它总是有用的。

以上是关于SetTimeout 在 Mongoose 模式后中间件中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

创建新模式对象后,Mongoose 引用的字段丢失

设置 Mongoose 模式进行身份验证后,如何使用 Mongodb 查询数据库?

在 angular-fullstack 中服务器重新启动后,Mongoose populate() 为空

更新猫鼬模式后,新输入的字段未插入已创建的集合中

[Mongoose]如何在 app.js 中使用两个不同的模式两次使用相同的模型

Mongoose在数据库中的模型名称后添加“s”? [复制]