You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Strapi V4忘记密码API定制:替换验证码为4位唯一数字

在Strapi V4中自定义忘记密码API的4位数字验证码

实现步骤

1. 生成4位唯一数字验证码的工具函数

在src/utils/generate-otp.js中创建工具函数,确保生成的验证码在有效期内唯一:

const { strapi } = require('@strapi/strapi');

module.exports = async () => {
  // 生成4位随机数字
  let otp = Math.floor(1000 + Math.random() * 9000).toString();
  
  // 检查有效期内是否存在重复验证码
  const existingToken = await strapi.db.query('plugin::users-permissions.reset-password-token').findOne({
    where: {
      token: otp,
      expires_at: { $gt: new Date() }
    }
  });

  // 重复则递归生成新验证码
  if (existingToken) {
    return module.exports();
  }

  return otp;
};

2. 覆盖Users-Permissions插件的Auth服务

在src/extensions/users-permissions/server/services/auth.js中创建自定义服务,替换默认的验证码生成逻辑:

const { validateForgotPassword } = require('@strapi/plugin-users-permissions/server/validators/auth');
const generateOTP = require('../../../../utils/generate-otp');

module.exports = (plugin) => {
  // 覆盖忘记密码处理方法
  plugin.forgotPassword = async (ctx) => {
    const { email } = ctx.request.body;
    await validateForgotPassword(ctx.request.body);

    const user = await strapi.db.query('plugin::users-permissions.user').findOne({
      where: { email },
    });

    if (!user) {
      return ctx.send({ ok: true }); // 避免泄露用户存在性
    }

    // 生成4位唯一验证码
    const otp = await generateOTP();
    // 设置1小时有效期(与默认保持一致)
    const expiresAt = new Date();
    expiresAt.setHours(expiresAt.getHours() + 1);

    // 删除用户旧的重置令牌
    await strapi.db.query('plugin::users-permissions.reset-password-token').deleteMany({
      where: { user: user.id }
    });

    // 保存新的验证码记录
    await strapi.db.query('plugin::users-permissions.reset-password-token').create({
      data: {
        user: user.id,
        token: otp,
        expires_at: expiresAt
      }
    });

    // 发送包含验证码的邮件
    await strapi.plugin('users-permissions').service('user').sendResetPasswordEmail(user, otp);

    return ctx.send({ ok: true });
  };

  return plugin;
};

3. 修改重置密码邮件模板

在src/extensions/users-permissions/server/templates/reset-password.html创建自定义邮件模板,替换默认的长令牌展示:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>重置密码</title>
  </head>
  <body>
    <p>您好 <%= user.username %>!</p>
    <p>您申请重置密码,请使用以下4位验证码完成操作:</p>
    <p><strong><%= token %></strong></p>
    <p>验证码1小时内有效。</p>
    <p>若非您本人操作,请忽略此邮件。</p>
  </body>
</html>

4. 适配邮件发送逻辑(可选)

如果需要自定义邮件发送细节,在src/extensions/users-permissions/server/services/user.js中覆盖发送方法:

module.exports = (plugin) => {
  plugin.sendResetPasswordEmail = async (user, token) => {
    const settings = await strapi.plugin('users-permissions').service('settings').getSettings('email');

    const subject = settings.reset_password.subject || '重置密码';
    const html = await strapi.plugin('users-permissions').service('email').render('reset-password', {
      user,
      token,
    });

    await strapi.plugin('email').service('email').send({
      to: user.email,
      from: settings.reset_password.from || settings.default.from,
      replyTo: settings.reset_password.replyTo || settings.default.replyTo,
      subject,
      html,
    });
  };

  return plugin;
};

注意事项

  • 修改完成后重启Strapi服务,确保自定义扩展生效
  • 测试时需验证验证码的唯一性,避免同一时段出现重复码
  • 可通过调整expiresAt的计算逻辑修改验证码有效期

内容的提问来源于stack exchange,提问作者Tayyab Shahzad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 16:27:23