重生之我在NestJS中使用jwt鉴权

可以使用已经存在的controllerservicemodule也可以新建,后续文章使用的都是新建的文件,整个过程就不使用dtoentities

1. 不管三七二十一,先使用命令创建controllerservicemodule

  • 创建controller: npx nest g co auth --no-spec
  • 创建service: npx nest g s auth --no-spec
  • 创建module: npx nest g mo auth --no-spec

**注意:**以上创建的文件会自动在app.module.ts中更新响应的配置,如果不需要自动导入,请在后面增加--skip-import

2. 在刚刚创建的auth/auth.controller.ts中增加loginprofile两个方法以及引入AuthService

typescript 复制代码
import { Body, Controller, Get, Post } from '@nestjs/common'
import { AuthService } from './auth.service'

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('login')
  login(@Body() params: any) {
    return this.authService.login(params)
  }

  @Get('profile')
  profile() {
    return this.authService.profile()
  }
}

3. 在auth/auth.service.ts中增加loginprofile两个方法

typescript 复制代码
import { Injectable } from '@nestjs/common'

@Injectable()
export class AuthService {
  private readonly users = [
    { id: 1, username: 'admin', password: '123456' },
    { id: 2, username: 'guest', password: '000000' }
  ]
  login(params: any) {
    console.log(params)
  }

  profile() {
    return 'profile'
  }
}

到这里,可以通过地址访问到创建的控制里面的方法了,比如访问:http://localhost:3000/auth/profile 可以得到一个 profile 字符串,接下来就是引入 jwt 了。

4. 引入 jwt 的依赖

  • 安装依赖: npm install @nestjs/jwt -S

5. 需要在刚刚创建的auth/auth.module.ts中引入 jwt 相关的配置

typescript 复制代码
import { Module } from '@nestjs/common'
import { JwtModule } from '@nestjs/jwt'

@Module({
  imports: [
    JwtModule.register({
      global: true, // 表示是否全局启用
      secret: '123456', // 密钥,先随便写死,后面再改
      signOptions: {
        // 这是配置项,有很多,可以点击去看 d.ts 的介绍
        expiresIn: '4h' // 过期时间,单位秒,4小时后过期,
      }
    })
  ]
})
export class AuthModule {}

这个配置里面比较重要的,基本上就是 secret 了,后面会写入到配置文件中,再引入进来,比较灵活的控制了。

6. 在auth/auth.service.tslogin方法中增加登录校验逻辑,并且生成 jwt 的 token 返回给前端,完整代码如下

需要引入一下JwtService这个服务

typescript 复制代码
import { Injectable } from '@nestjs/common'
import { JwtService } from '@nestjs/jwt'

@Injectable()
export class AuthService {
  private readonly users = [
    { id: 1, username: 'admin', password: '123456' },
    { id: 2, username: 'guest', password: '000000' }
  ]

  // 引入 JwtService 服务,用于生成 token 和验证 token 的有效性
  constructor(private readonly jwtService: JwtService) {}

  login(params: any) {
    const { username, password } = params
    const user = this.users.find(user => user.username === username)
    console.log(params)
    if (user && user.password === password) {
      // 生成jwt token
      const token = this.jwtService.sign({
        sub: user.id,
        username: user.username
      })
      return {
        code: 200,
        data: { token },
        msg: '登录成功'
      }
    }

    // 直接返回账号密码错误信息
    return {
      code: -1,
      msg: '账号或密码错误'
    }
  }

  profile() {
    return 'profile'
  }
}

启动服务,使用命令测试一下请求: curl -X POST http://localhost:3000/auth/login -d '{"username": "admin", "password": "123456"}' -H "Content-Type: application/json"

到现在为止,我们已经成功的生成了一个 token,接下来就是如何让这个 token 在接口中生效的问题,那么需要创建一个 guard。

7. 使用命令创建auth.guard.ts,并在里面写入相关内容

  1. 使用命令创建 npx nest g gu auth
  2. auth/auth.guard.ts中写入鉴权相关的内容,代码如下
typescript 复制代码
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common'
import { JwtService } from '@nestjs/jwt'
import { Reflector } from '@nestjs/core'
import { Observable } from 'rxjs'
import { Request } from 'express'
import { jwtSecretConstant } from 'src/config/jwt'

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private readonly jwtService: JwtService, private readonly reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
    // 这个可以先不看,和后面使用注解有关系
    const isPublic = this.reflector.getAllAndOverride<boolean>('isPublic', [context.getHandler(), context.getClass()])

    if (isPublic) return true

    const request =  context.switchToHttp().getRequest()
    // 主要是这里解析header中的 Authorization 这个值
    const token = this.extractTokenFromHeader(request)
    if (!token) {
      throw new UnauthorizedException('Token not found')
    }
    return this.jwtService
      .verifyAsync(token, {
        secret: '123456' // 这里和之前的auth.module.ts中保持一致
      })
      .then(payload => {
        request['user'] = payload // 这一步可以不要,主要是让用户在controller中可以获取一下用户信息之类的

        return true
      })
      .catch(() => {
        throw new UnauthorizedException('Invalid token')
      })
  }

  extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') || []
    return type === 'Bearer' ? token : undefined
  }
}

现在已经创建好了 guard,该如何使用呢,有两种方式:① 使用注解的方式;② 使用providers配置全局的

8.如何让刚刚创建的auth.guard.ts生效

  1. 使用UseGuards注解的方式

auth/auth.controller.ts中的profile方法上面添加注解,完整代码如下

typescript 复制代码
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common'
import { AuthService } from './auth.service'
import { AuthGuard } from './auth.guard'

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('login')
  login(@Body() params: any) {
    return this.authService.login(params)
  }

  @Get('profile')
  @UseGuards(AuthGuard) // 主要使用这个注解,需要引入一下
  profile() {
    return this.authService.profile()
  }
}

现在已经使用起来了,我们通过命令或者浏览器访问一下: http://localhost:3000/auth/profile 这地址,会发现访问不了了,返回了 401 ,说明我们没有权限访问;我们可以在 headers 中携带之前生成的 token 再次访问试一下,命令访问方式:curl -X GET http://localhost:3000/auth/profile -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJpYXQiOjE3MjEyODg3OTIsImV4cCI6MTcyMTMwMzE5Mn0.D5d8gEUtadfBM7afE5tj_hmYkG-6N2_Hf8IyKe45RBY',此时能正常返回 profile 了,说明 jwt 生效了

  1. 使用全局配置的方式(个人比较推荐)

更新auth/auth.module.ts的配置文件,完整代码如下

typescript 复制代码
import { Module } from '@nestjs/common'
import { APP_GUARD } from '@nestjs/core'
import { JwtModule } from '@nestjs/jwt'
import { AuthGuard } from './auth.guard'

@Module({
  imports: [
    JwtModule.register({
      global: true, // 表示是否全局启用
      secret: '123456', // 密钥,先随便写死,后面再改
      signOptions: {
        // 这是配置项,有很多,可以点击去看 d.ts 的介绍
        expiresIn: '4h' // 过期时间,单位秒,4小时后过期,
      }
    })
  ],
  providers: [{ provide: APP_GUARD, useClass: AuthGuard }] // 主要是添加这一行
})
export class AuthModule {}

可以把 AuthController 里面的 profile 上面的 UseGuard 注解删除试一下,和刚刚的效果是一模一样的;到这个地方,所有控制器的所有的方法都需要传入 Authorization 这个 header 头,显然是不太合理的。比如刚刚的登录方法,是不需要鉴权的,所以需要排除一下,可以创建一个全局的注解,之前在auth/auth.guard.ts中对注解的判断就有效了。

9. 创建全局注解,排除不需要鉴权的方法

  • 使用命令创建nest g d auth,会在 src 根目录创建一个auth.decorator.ts的文件,删除里面的内容,写入以下内容
typescript 复制代码
import { SetMetadata } from '@nestjs/common'

// 创建一个注解,外边可以用 @Public() 使用它
export const Public = () => SetMetadata('isPublic', true)

注解创建完成了,我们到auth/auth.controller.ts中的login方法上面添加一行: @Public(),添加的时候需要引入一下import { Public } from './decorators/public.decorator';;现在我们方法登录的这个方法,就不会进行权限校验了。到此,jwt 鉴权的流程基本上就结束了,前面使用的secret可以弄成全局的配置文件

10. 将 jwt 用到的 secret 弄成全局的配置文件

在 src 下面创建一个 config 目录,再在 config 下面创建一个jwt.ts文件,写入以下内容

typescript 复制代码
export const jwtSecretConstant = '123456' // 这个是 secret
export const jwtExpireConstant = '5d' // 这个是过期时间

创建好了之后,直接去 auth.module.tsauth.guard.ts 中将 secretexpiresIn 更新以下即可。

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax