nestjs守卫/全局守卫校验jwt

一、守卫

目标

部分接口需要用户登录后才可以访问,用户登录后可以颁发 jwt_token 给前端,前端在调用需要鉴权的接口,需要在请求头添加 jwt_token,后端校验通过才能继续访问,否则返回403无权访问

创建守卫 anth

安装依赖
npm i @nestjs/jwt -S
配置 jwt 常量参数 constants/index.ts
// jwt秘钥,固定随机字符串
export const JWT_SECRET = "NODE_TEST_SECRET";

// jwt有效期 Eg: "60s", "3h", "2d"
export const JWT_EXPIRES = "2h"; // 2小时
创建 auth 模块 auth/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { JWT_SECRET, JWT_EXPIRES } from '../constants';

@Module({
  imports: [
    JwtModule.register({
      secret: JWT_SECRET, // jwt秘钥
      signOptions: {
        expiresIn: JWT_EXPIRES, // jwt有效期
      }
    })
  ],
  providers: [AuthService],
  exports: [AuthService]
})
export class AuthModule {}
创建 auth 服务 auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(private readonly jwtService: JwtService) {}

  // 创建jwt_token
  async createToken(userInfo) {
    // 将用户信息(userId、name)存放到 jwt 中
    return {
      access_token: this.jwtService.sign(userInfo)
    };
  }

  // 校验登录状态
  validateToken(token) {
    if (!token) {
      return false;
    }
    try {
      return this.jwtService.verify(token);
    } catch {
      return false;
    }
  }
}
创建 auth 守卫 auth/auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService) {}

  canActivate(context: ExecutionContext) {
    const request = context.switchToHttp().getRequest();
    const token = request.headers['authorization']; // 从请求头中获取 token
    if (!token) {
      return false;
    } else {
      return this.authService.validateToken(token); // 如果 token 有效,返回 true,允许访问资源
    }
  }
}

使用守卫 auth

需要鉴权的模块添加 auth 模块

api.module.ts

...
import { AuthModule } from '../auth/auth.module';

@Module({
  imports: [AuthModule],
  ...
})
export class ApiModule {}
需要鉴权的接口引入守卫

api.controller.ts

...
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '../auth/auth.guard';

@Controller('api')
export class ApiController {
  constructor(private readonly apiService: ApiService) {}

  @Get('getUserInfo')
  @UseGuards(AuthGuard) // 需要鉴权的接口添加UseGuards
  getUserInfo(): any {
    return this.apiService.getUserInfo();
  }
}
登录

调用 auth/auth.service.ts 中的 createToken 生成 jwt_token 返回给前端

前端在请求头添加 authorization

如果请求头没有 authorization,或者 authorization 失效,则返回 403 Forbidden

二、全局守卫

目标

由于单个守卫需要引入守卫模块、守卫方法,并且针对每一个接口加 @UseGuards 装饰器,使用起来比较繁琐。项目中经常遇到非常多的接口都需要鉴权,所以就需要使用全局守卫来校验需要鉴权的接口。

注意:获取与解析 jwt_token 的方法上面守卫的方法一致,auth/auth.service,此处不再赘述。

创建全局守卫

配置需要鉴权的接口常量 constants/index.ts
// 需要校验 jwt 的 url 列表
export const ValidUrlList = [
  '/api/getUserInfo',
  '/api/product/buy',
  '/api/queryUserWallet'
];
新建全局守卫 global.guard.ts
import { Injectable, NestInterceptor, ExecutionContext, HttpException, HttpStatus } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AuthService } from './auth/auth.service';
import { ValidUrlList } from './constants';

@Injectable()
export class GlobalGuard implements NestInterceptor {
  constructor(private authService: AuthService) {}
  intercept(context: ExecutionContext, next): Observable<any> {
    // 在这里执行全局守卫逻辑
    const request = context.switchToHttp().getRequest();
    // 从请求头中获取 token
    const token = request.headers['authorization'];
    // 请求的url,用于判断是否需要校验jwt
    const url = request.url;
    if (ValidUrlList.includes(url)) {
      // 需要鉴权才能访问的接口
      const validRes = this.authService.validateToken(token);
      if (!validRes) {
        return next.handle().pipe(map(() => {
          // 如果没有提供令牌,返回错误响应或执行其他逻辑
          return new HttpException('Forbidden', HttpStatus.FORBIDDEN);
        }));
      }
    }
    // 不需要校验的接口、校验通过的接口直接放行
    return next.handle();
  }
}

在 app.module.ts 配置全局守卫

// ...
import { APP_INTERCEPTOR } from '@nestjs/core';
import { JwtModule } from '@nestjs/jwt';
import { GlobalGuard } from './global.guard';
import { JWT_SECRET, JWT_EXPIRES } from './constants';
import { AuthService } from './auth/auth.service';

@Module({
  imports: [
    JwtModule.register({
      secret: JWT_SECRET, // jwt秘钥
      signOptions: {
        expiresIn: JWT_EXPIRES, // jwt有效期
      }
    }),
  ],
  providers: [
    {
      provide: APP_INTERCEPTOR,
      useClass: GlobalGuard,
    },
    AuthService
  ],
})
export class AppModule {}

三、swagger文档调用需要鉴权的接口

目标

接口添加 jwt 鉴权后,接口文档调用接口请求头没有添加 authorization,请求会返回403。为此,需要给文档需要鉴权的接口请求头也添加 authorization

步骤

main.ts 配置 BearerAuth 校验
const config = new DocumentBuilder()
  .setTitle('接口文档')
  .setDescription('接口文档描述')
  .setVersion('1.0')
  .addBearerAuth() // 注意此处:文档添加BearerAuth
  .build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api-docs', app, document);
在需要鉴权的接口添加 @ApiBearerAuth() 装饰器
import { ApiBearerAuth } from '@nestjs/swagger';

@Controller('api')
@ApiBearerAuth() // 在此处添加,表示/api/的接口请求头全都需要添加authorization
export class ApiController {

  @Get('getUserInfo')
  @UseGuards(AuthGuard)
  @ApiBearerAuth() // 在此处添加,表示当前接口请求头需要添加authorization
  getUserInfo(): any {
    return this.apiService.getUserInfo();
  }
}
使用

先调用登录接口获取到 jwt_token

点击文档顶部Authorize按钮

输入获取到的 jwt_token,并点击Authorize,然后关闭弹窗

再调用需要鉴权的接口,就可以鉴权通过了

注意:请求头的 Authorization 参数会在最前面添加 Bearer 字符,可以在守卫中将此字符移除

this.jwtService.verify(token.replace('Bearer ', ''))

相关推荐

  1. Angular: resolve 守卫

    2023-12-18 15:24:02       40 阅读
  2. vue - 路由守卫

    2023-12-18 15:24:02       14 阅读
  3. Vue路由守卫笔记

    2023-12-18 15:24:02       36 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-18 15:24:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-18 15:24:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-18 15:24:02       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-18 15:24:02       20 阅读

热门阅读

  1. postMessage解决跨域、消息传递

    2023-12-18 15:24:02       35 阅读
  2. golang os 包用法

    2023-12-18 15:24:02       42 阅读
  3. 医保dip质控系统如何实现医保控费?

    2023-12-18 15:24:02       40 阅读
  4. 医保DRG/DIP智能分析质控系统

    2023-12-18 15:24:02       36 阅读
  5. UE5Console 控制台命令

    2023-12-18 15:24:02       43 阅读
  6. UE5中C++对蓝图类的软引用方法

    2023-12-18 15:24:02       34 阅读
  7. 【uniapp小程序-分享】

    2023-12-18 15:24:02       34 阅读
  8. GoLand couldn‘t start listener: listen tcp 198.18.0.45:53638

    2023-12-18 15:24:02       38 阅读
  9. 01到底应该怎么理解“平均负载”

    2023-12-18 15:24:02       26 阅读