NestJS 从零入门完整教程(进阶篇):JWT 登录实现,可直接投产
大家好,今天我们直接上手企业刚需:NestJS 完整 JWT 登录鉴权体系。
不讲废话、不兜概念,全程只做一件事:把「账号密码登录 → 颁发 Token → 全局接口拦截 → 自动识别登录态」全套代码跑通。写完就能直接塞进你后台项目里投产。
适合人群:刚学完 Nest 基础模块、准备写登录接口、准备对接前端 Vue/React 的后端/全栈开发者。
前置环境:已经有可运行的 Nest 项目,会简单接口调试即可。
一、整体思路先捋一遍(看懂再写代码,不踩坑)
整套登录鉴权就五步,逻辑非常固定:
1)安装 JWT、密码加密、鉴权相关依赖包 2)全局配置 JWT 密钥 + 过期时间 3)写一套 JWT 校验策略:专门用来解析、验 Token 4)开发登录接口:校验用户名密码 → 成功下发 Token 5)路由加守卫:接口必须带合法 Token 才能访问,自动拦截未登录请求
流程通了,后面做单点登录、刷新令牌、权限角色,全部都是扩展,底子不变。
二、第一步:安装全套 JWT 必备依赖
直接复制执行,一次性装好所有需要用到的核心包 + 类型包:
npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcryptjs
npm install -D @types/passport-jwt @types/bcryptjs
简单说下每个包干嘛用:
-
@nestjs/jwt:Nest 官方 JWT 工具,生成、解析、校验 Token 全靠它
-
@nestjs/passport + passport-jwt:标准化鉴权方案,统一拦截请求、统一验票
-
bcryptjs:密码单向加密、比对,绝对不存明文密码,安全生产必备
三、第二步:全局注册 JWT 模块(一次性全局生效)
打开根模块 app\.module\.ts,直接全局配置 JWT,整个项目所有模块都能用,不用重复引入。
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
@Module({
imports: [
UserModule,
// 注册默认鉴权策略为 jwt
PassportModule.register({ defaultStrategy: 'jwt' }),
// 全局 JWT 配置
JwtModule.register({
secret: 'server_private_secret_key_2026',
signOptions: { expiresIn: '2h' },
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
小提示:这里密钥本地开发随便写,上线一定要放到环境变量,绝对不要硬编码提交 Git。
四、第三步:核心能力——编写 JWT 校验策略
这一步是鉴权心脏:所有请求来了,都会先走这里校验 Token 合不合法、有没有过期、是不是伪造的。
在 src 下新建文件夹auth,新建文件 auth/jwt\.strategy\.ts。
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { UserService } from '../user/user.service';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private readonly userService: UserService) {
super({
// 固定写法:从 Header Authorization 里拿 Bearer Token
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
// 和 app.module 里的密钥保持完全一致
secretOrKey: 'server_private_secret_key_2026',
// 开启过期校验
ignoreExpiration: false,
});
}
// 校验通过后自动执行,挂载用户信息到 req.user
async validate(payload: any) {
// payload 就是 Token 解密出来的内容
const userId = payload.sub;
// 根据用户 id 查询真实用户
const user = await this.userService.findById(userId);
if (!user) {
throw new Error('登录身份无效,请重新登录');
}
// 返回的值直接塞进 req.user,后面控制器直接能用
return user;
}
}
五、第四步:快速生成 Auth 模块,写登录业务
终端一行命令,自动建好 auth 全套结构:
nest g module auth
nest g service auth
nest g controller auth
5.1 编写 AuthService:密码比对 + 下发 Token
打开 auth/auth\.service\.ts,写真实登录逻辑:
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UserService } from '../user/user.service';
import * as bcrypt from 'bcryptjs';
@Injectable()
export class AuthService {
constructor(
private readonly userService: UserService,
private readonly jwtService: JwtService,
) {}
async login(username: string, password: string) {
// 1. 根据用户名查库
const user = await this.userService.findByUsername(username);
if (!user) {
throw new UnauthorizedException('用户名或密码错误');
}
// 2. 比对加密密码
const isOk = await bcrypt.compare(password, user.password);
if (!isOk) {
throw new UnauthorizedException('用户名或密码错误');
}
// 3. 签发 JWT Token
const token = this.jwtService.sign({
sub: user.id,
username: user.username,
});
// 返回前端需要的全部信息
return {
token,
userInfo: {
id: user.id,
username: user.username,
},
};
}
}
5.2 编写登录接口 Controller
打开 auth/auth\.controller\.ts,暴露 POST 登录接口:
import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('login')
login(@Body() body: { username: string; password: string }) {
return this.authService.login(body.username, body.password);
}
}
5.3 把策略注册到 AuthModule
打开 auth/auth\.module\.ts,把 JwtStrategy 挂载进去:
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './jwt.strategy';
import { UserModule } from '../user/user.module';
import { PassportModule } from '@nestjs/passport';
@Module({
imports: [UserModule, PassportModule],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}
六、第五步:补充 User 层模拟数据(直接可测)
打开 user/user\.service\.ts,写好查询方法,内置一个测试账号:admin / 123456
import { Injectable } from '@nestjs/common';
import * as bcrypt from 'bcryptjs';
// 模拟数据库用户表
const mockUserList = [
{
id: 1,
username: 'admin',
password: '$2a$10$n9VrR0w7xQ5g8kFzJ0aX/.mP0q2rT5yU7iO8pA9sD0fG1hJ2kL3',
},
];
@Injectable()
export class UserService {
async findByUsername(username: string) {
return mockUserList.find(item => item.username === username);
}
async findById(id: number) {
return mockUserList.find(item => item.id === id);
}
}
七、第六步:接口加拦截器,实现必须登录才能访问
随便找一个业务接口,加上 @UseGuards(AuthGuard('jwt')),一秒开启登录校验。
import { Controller, Get, UseGuards, Req } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Controller('user')
export class UserController {
// 带 Token 才能访问
@Get('profile')
@UseGuards(AuthGuard('jwt'))
getProfile(@Req() req) {
// req.user 就是当前登录用户信息,直接用
return {
code: 200,
data: req.user,
message: '获取当前登录用户成功',
};
}
}
八、直接测试,一键跑通全流程
1、启动项目:npm run start:dev
2、POST 请求 /auth/login,参数:
{
"username": "admin",
"password": "123456"
}
3、成功返回合法 Token
4、请求头携带 Authorization: Bearer 你的Token 访问/profile,正常拿用户信息
5、不带 Token 直接 401 拦截,鉴权生效
九、上线必须遵守的 5 条安全规范
1、JWT 密钥绝对不能写死代码,全部放入环境变量配置; 2、密码全程 bcrypt 加密入库,禁止任何明文传输、明文存储; 3、Token 载荷只放用户 ID、账号,不放手机号、密码、隐私数据; 4、合理设置过期时间,后台配合做 Token 刷新机制; 5、线上全部接口统一全局守卫,只开放登录、注册、验证码白名单。
十、总结
这篇下来,你直接拥有了企业级可用的 NestJS JWT 登录整套能力:依赖安装 → JWT 全局配置 → 策略校验 → 登录下发令牌 → 接口自动拦截。代码全部可直接复用,无需二次改造,一键发布上线。
后续想看:环境变量全局配置、刷新 Token、角色权限、对接 MySQL,评论区告诉我就行。
(注:文档部分内容可能由 AI 生成)