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

如何无需Firebase登录密码,为Express API实现用户专属永久令牌认证?

Express JS API:单用户永久有效令牌的安全实现方案

核心原则

永久有效且单用户绑定的令牌,核心是可验证、可撤销、防窃取——别用无状态的JWT(JWT一旦签发无法主动撤销,除非维护庞大的黑名单),必须结合数据库做状态化存储,同时从令牌生成、传输、存储全链路做安全加固。

具体实现步骤

  • 生成强随机令牌
    直接生成足够复杂的随机字符串,用Node.js内置的crypto模块就行,确保碰撞概率可以忽略:

    const crypto = require('crypto');
    function generatePermanentToken() {
      return crypto.randomBytes(32).toString('hex'); // 生成64位十六进制字符串,复杂度足够
    }
    
  • 安全存储令牌
    在数据库建user_tokens表(或集合),字段至少包括:

    • hashed_token:令牌的哈希值(绝对不能明文存令牌)
    • user_id:关联的用户ID
    • created_at:创建时间
    • device_info(可选):记录令牌生成时的设备/IP,用于后续异常验证

    存储时用bcrypt加盐哈希令牌,避免泄露后被直接使用:

    const bcrypt = require('bcrypt');
    async function saveToken(userId, rawToken) {
      const hashedToken = await bcrypt.hash(rawToken, 10);
      // 以Mongoose为例存入数据库:
      await UserToken.create({ user_id: userId, hashed_token: hashedToken });
      return rawToken; // 返回原始令牌给用户,数据库只存哈希值
    }
    
  • 认证中间件实现
    前端请求时,令牌可以放在Authorization: Bearer <token>头,或者用HttpOnly、Secure的Cookie(更安全,防XSS)。中间件逻辑如下:

    const bcrypt = require('bcrypt');
    const UserToken = require('./models/userToken');
    const User = require('./models/user');
    
    async function authenticateToken(req, res, next) {
      // 从请求头获取令牌
      const authHeader = req.headers['authorization'];
      const token = authHeader && authHeader.split(' ')[1];
    
      if (!token) return res.sendStatus(401);
    
      try {
        // 遍历令牌记录匹配哈希值(可优化:给hashed_token加索引提升效率)
        const tokenDocs = await UserToken.find();
        let matchedUser = null;
        for (const doc of tokenDocs) {
          const isMatch = await bcrypt.compare(token, doc.hashed_token);
          if (isMatch) {
            matchedUser = await User.findById(doc.user_id);
            break;
          }
        }
    
        if (!matchedUser) return res.sendStatus(403);
    
        req.user = matchedUser; // 将用户信息挂载到请求对象
        next();
      } catch (err) {
        res.sendStatus(500);
      }
    }
    
  • 权限校验(关键!)
    令牌验证通过后,必须确保用户只能访问自己的私有数据。比如获取私信的路由:

    app.get('/api/private/messages', authenticateToken, async (req, res) => {
      // 只查询当前用户作为收件人的私信
      const messages = await PrivateMessage.find({ recipient_id: req.user._id });
      res.json(messages);
    });
    
  • 令牌撤销
    用户删除账号时,直接删除该用户对应的所有令牌记录即可:

    async function revokeUserTokens(userId) {
      await UserToken.deleteMany({ user_id: userId });
    }
    

    也可以加is_revoked字段做软删除,方便后续排查问题。

额外安全加固

  • 令牌绑定设备/IP:生成令牌时记录设备指纹(比如User-Agent+IP的哈希),验证时检查当前请求的设备信息是否匹配,防止令牌被盗用后跨设备使用。
  • 用HttpOnly Cookie存令牌:避免XSS攻击窃取令牌,同时开启Secure(仅HTTPS传输)和SameSite=Strict属性。
  • 异常登录告警:如果发现同一令牌在不同设备/IP使用,给用户发邮件/短信告警,支持主动撤销令牌。
  • 定期提醒换令牌:即使是永久令牌,也建议用户每隔一段时间手动刷新,降低被盗用的风险。

内容的提问来源于stack exchange,提问作者Cartoon guy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 03:12:50