后端
NestJS
JWT
Node.js

NestJS 从零入门完整教程(进阶篇):JWT 登录实现,可直接投产

2025/12/23·阅读约 4 分钟

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 生成)