xlt-token 1.1:给 NestJS 补上 Sa-Token 式鉴权能力

一、项目背景 + 项目介绍

背景:NestJS 鉴权常见痛点

在 NestJS 项目里做登录鉴权,很多人会遇到同一组问题:

  • Guard + Session/JWT 各写一套,顶号、踢人、活跃过期要自己拼 Redis 键
  • 多端登录语义绕不清:PC 和 App 能不能同时在线?同端能不能顶号?
  • 敏感操作(转账、改密)只有一级登录,缺少「二次确认」机制
  • 运维侧查在线用户、强制下线、审计登录事件,往往要重复造轮子

Java 生态有 Sa-Token 把这些问题封装成统一 API;Node 侧长期缺少一个 NestJS 原生、语义对齐、可插拔 的同类方案。

xlt-token 是什么?

xlt-token 是一个面向 NestJS 的轻量 Token 鉴权库,灵感来自 Sa-Token,MIT 开源。

链接 地址
在线文档 xiaolangtou.github.io/xlt-token/
GitHub github.com/xiaoLangtou...
npm pnpm add xlt-token

核心设计:

  • 可插拔:Store(内存 / Redis)、Token 策略(UUID / JWT)均可替换
  • 双形态 APIStpLogic 依赖注入 + StpUtil 静态门面
  • 声明式开发 :Guard + 装饰器(@XltIgnore@LoginId@XltCheckPermission 等)
  • 与 1.0 兼容:1.1 新能力全部 opt-in,老项目升级不破坏现有 API

二、项目如何使用(5 分钟接入)

安装

bash 复制代码
pnpm add xlt-token
# 生产多实例建议
pnpm add redis

注册模块 + 全局守卫

ts 复制代码
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { XltTokenModule, XltTokenGuard } from 'xlt-token';

@Module({
  imports: [
    XltTokenModule.forRoot({
      isGlobal: true,
      config: {
        tokenName: 'authorization',
        timeout: 7 * 24 * 60 * 60,
      },
    }),
  ],
  providers: [{ provide: APP_GUARD, useClass: XltTokenGuard }],
})
export class AppModule {}

登录 / 登出 / 取当前用户

ts 复制代码
import { Controller, Post, Body } from '@nestjs/common';
import { StpUtil, XltIgnore, LoginId, TokenValue } from 'xlt-token';

@Controller('auth')
export class AuthController {
  @XltIgnore()
  @Post('login')
  async login(@Body() dto: { username: string }) {
    const token = await StpUtil.login(dto.username);
    return { token };
  }

  @Post('logout')
  async logout(@TokenValue() token: string) {
    await StpUtil.logout(token);
    return { ok: true };
  }

  @Post('me')
  me(@LoginId() loginId: string) {
    return { loginId };
  }
}

默认 defaultCheck: true所有接口需登录 ,公开路由加 @XltIgnore() 即可。

客户端请求时带上 Header:

http 复制代码
Authorization: Bearer <token>

更多配置、Redis 存储、异步 forRootAsync在线文档 · 快速开始


三、本版本做了哪些内容(1.1 四大方向)

1.1 的实现设计(13-1.1.0-implementation-design.md)按 四个 Milestone 落地,与 Roadmap 对齐:

方向 解决什么问题 主要交付
① 多端登录 PC / App 能否共存、同端能否顶号 device 参数、deviceConcurrent、设备列表与按端踢人
② 二级认证 + 临时 Token 敏感操作二次确认、邮件链接一次性授权 openSafe / @XltCheckSafecreateTempToken 系列
③ JWT Strategy 既要 JWT 验签,又要踢人/顶号/活跃过期 内置 JwtStrategy + jti 黑名单
④ Hooks + 观测性 在线用户、强制下线、登录审计 keys() 扫描、getOnlineLoginIds、生命周期 Hooks

设计原则(贯穿全版本):

  • 与 1.0 API 完全兼容,新字段/新 API 均为可选
  • 不传 device 时等价 device: 'default',旧 session 键可迁移
  • 不绑业务:权限数据仍由 StpInterface 实现,库不做 CRUD

四、功能点 · 怎么用 · 完成了什么

4.1 多端登录

完成了什么:

  • Session 键升级为 session:${loginId}:${device},并增加 session-list 设备索引
  • 新增 deviceConcurrent,与 isConcurrent / isShare 组合控制互踢范围
  • 提供 getDeviceListkickoutByDevicekickoutByTokenforceLogout

典型配置 --- PC 与 App 互不影响:

ts 复制代码
XltTokenModule.forRoot({
  config: {
    deviceConcurrent: true,
    isConcurrent: true,
    isShare: false,
  },
});

怎么用:

ts 复制代码
// 登录时指定设备
const pcToken = await stp.login(userId, { device: 'pc' });
const appToken = await stp.login(userId, { device: 'app' });

// 管理端:查设备、踢指定端
const devices = await stp.getDeviceList(userId);
await stp.kickoutByDevice(userId, 'pc');  // 只踢 PC
await stp.forceLogout(userId);             // 全端下线

📄 详见文档:14 · 多端登录


4.2 二级认证(Safe)+ 临时 Token

完成了什么:

  • 存储键:safe:${token}:${business} 标记安全窗口;temp-token:${tempToken} 存业务 payload
  • 新增 NotSafeException(403)、装饰器 @XltCheckSafe
  • Guard 在 checkLogin 成功后自动消费 Safe 元数据

二级认证 --- 转账前验证码:

ts 复制代码
// Step 1:验证通过后打开 300 秒窗口
await stp.openSafe(token, 'pay', 300);

// Step 2:敏感接口
@XltCheckSafe('pay')
@Post('transfer')
transfer() { /* 未 openSafe 则 403 */ }

临时 Token --- 邮件重置密码:

ts 复制代码
const tempToken = await stp.createTempToken(`resetPwd:${userId}`, 1800);
const link = `https://app.com/reset?t=${tempToken}`;

// 用户点链接
const value = await stp.parseTempToken(tempToken);
await stp.deleteTempToken(tempToken);  // 一次性消费

📄 详见文档:15 · 二级认证


4.3 内置 JWT Strategy

完成了什么:

  • 新增 JwtStrategy:payload 约定 sub(loginId)+ jti(会话 ID)
  • 鉴权路径:本地 verifyToken + 查 jwt-blacklist:${jti}(被踢/被顶才写黑名单)
  • 保留 xlt-token 语义:顶号、踢人、活跃过期仍由 Store 控制(有状态 JWT,非纯无状态)

怎么用:

ts 复制代码
import { XltTokenModule, JwtStrategy } from 'xlt-token';

XltTokenModule.forRoot({
  strategy: { useClass: JwtStrategy },
  config: {
    jwt: {
      secret: process.env.JWT_SECRET!,
      algorithm: 'HS256',
    },
    timeout: 86400,
  },
});

需自行安装:pnpm add jsonwebtoken

📄 详见文档:16 · JWT 策略


4.4 Hooks + 在线观测

完成了什么:

  • Store 接口扩展 keys(pattern)(Redis 用 SCAN,避免 KEYS 阻塞)
  • getOnlineLoginIds / getOnlineCount 扫描 session-list:*
  • Hooks:onLogin / onKickout 等,异常不影响主流程

怎么用:

ts 复制代码
XltTokenModule.forRoot({
  hooks: {
    onLogin: (loginId, token, device) => {
      audit.log('login', { loginId, device });
    },
    onKickout: (loginId, token) => {
      websocket.notify(loginId, '您已被强制下线');
    },
  },
});

// 运维 API
const count = await stp.getOnlineCount();
const users = await stp.getOnlineLoginIds({ page: 0, pageSize: 50 });

📄 详见文档:17 · Hooks 与观测性


4.5 1.0 能力回顾(本版本继续可用)

能力 用法要点
顶号 / 踢人 isConcurrent: false 顶号;kickout() 返回 KICK_OUT 而非笼统 401
活跃过期 activeTimeout + 每次请求刷新 lastActive
权限 / 角色 @XltCheckPermission / @XltCheckRole + 实现 StpInterface
会话扩展 StpUtil.getSession(loginId).set(key, value)
自定义 Guard 继承 XltAbstractLoginGuard,重写 onAuthSuccess 加载 request.user

五、可靠性:测试怎么保障这些功能

我们不为「能跑通 Demo」买单,每个 Milestone 都有对应单测 / E2E

测试规模(当前实测)

指标 数值
用例总数 274
单测 211(10 个 spec)
E2E 63(11 个 spec)
单测覆盖率 96.03% Stmts / 91.97% Branch
E2E 覆盖率 96.08% Stmts / 91.39% Branch

1.1 功能与测试对应关系

功能模块 单测 E2E
多端登录 / session-list stp-logic.spec.ts multi-device.e2e-spec.ts(13 用例)
二级认证 + 临时 Token stp-logic.spec.ts secondary-auth.e2e-spec.ts(7 用例)
JWT + 黑名单 stp-logic.jwt.spec.ts(13 用例) jwt.e2e-spec.ts(6 用例)
Store keys() 扫描 memory-store / redis-store spec 在线列表场景
Guard / 装饰器 各模块单测 app-guard / permission / custom-guard
模块注册 / 静态门面 xlt-token.module spec forroot-async / static-facade

核心文件 stp-logic.ts 单测 70+ 用例,覆盖 login 分支、JWT 分支、Safe、临时 Token、活跃过期等路径。

本地一键验证

bash 复制代码
pnpm test          # 211 单测
pnpm test:e2e      # 63 E2E
pnpm test:cov      # 覆盖率报告

文档站代码块已接入 Twoslash,悬停即可看类型,和测试一起保证「文档示例 ≠ 假代码」。


结语

xlt-token 1.0 把 NestJS + Sa-Token 语义 的地基打好;1.1 补齐了生产里最常要的四个能力:多端、Safe、JWT、观测性

如果你也在 NestJS 里维护鉴权层,欢迎:

一行 forRoot,剩下的交给库。

相关推荐
xUxIAOrUIII10 小时前
Dive into Claude Code 系列文章 - Part One
人工智能·ai·typescript
Shiy_17 小时前
用 TypeScript 验证三门问题:为什么换门胜率是 2/3?
算法·typescript
kkoral19 小时前
Vue3 图片标框功能实现方案
前端·vue.js·vscode·typescript
森G1 天前
TypeScript 基础类型
开发语言·typescript
烛阴2 天前
用 MCP 调教 AI 代理:让 Cocos Creator 3.8.8 核心逻辑一键全自动生成
typescript·cocos creator
Rain5092 天前
mini-cc 技术栈:跟着 Claude Code 先选 TypeScript + React + Ink
前端·javascript·react.js·typescript·node.js·ai编程
yantuguiguziPGJ2 天前
WeMed:一个医疗垂直领域大模型 问答系统的 Taro 小程序
typescript·node.js
tedcloud1232 天前
wifi-densepose部署教程:构建无线人体感知系统
服务器·javascript·网络·typescript·ocr
奇奇怪怪的问题3 天前
学习ts笔记(二):属性修饰符,泛型,接口
前端·typescript