Nestjs JWT_Nestjs 认证

NestJs仿小米商城项目实战视频教程(视频+课件+源码) https://www.itying.com/goods-1139.html


认证(Authentication)

身份验证是大多数现有应用程序的重要组成部分。有许多不同的方法、策略和方法来处理用户授权。任何项目采用的方法取决于其特定的应用程序要求。本章介绍了几种可以适应各种不同要求的身份验证方法。

passport是目前最流行的node.js认证库,为社区所熟知,并相继应用于许多生产应用中。将此工具与Nest框架集成起来非常简单。为了演示,我们将设置 passport-http-bearer 和 passport-jwt 策略。

Passport是最流行的node.js身份验证库,为社区所熟知,并成功地应用于许多生产应用程序中。将这个库与使用@nestjs/passport模块的Nest应用程序集成起来非常简单。在较高级别,Passport执行一系列步骤以:

  • 通过验证用户的"证"(例如用户名/密码、JSON Web令牌(JWT)或身份提供者的身份令牌)来验证用户的身份。
  • 管理经过身份验证的状态(通过发出可移植的令牌,例如JWT,或创建一个Express会话)
  • 将有关经过身份验证的用户的信息附加到请求对象,以便在路由处理程序中进一步使用

Passport具有丰富的策略生态系统,可实施各种身份验证机制。 尽管概念上很简单,但是您可以选择的Passport策略集非常多,并且有很多种类。Passport将这些不同的步骤抽象为标准模式,而@nestjs/passport模块将该模式包装并标准化为熟悉的Nest构造。

在本章中,我们将使用这些强大而灵活的模块为RESTful API服务器实现完整的端到端身份验证解决方案。您可以使用这里描述的概念来实现Passport策略,以定制您的身份验证方案。您可以按照本章中的步骤来构建这个完整的示例。您可以在这里找到带有完整示例应用程序的存储库。

身份认证

让我们充实一下我们的需求。对于此用例,客户端将首先使用用户名和密码进行身份验证。一旦通过身份验证,服务器将发出JWT,该JWT可以在后续请求的授权头中作为token发送,以验证身份验证。我们还将创建一个受保护的路由,该路由仅对包含有效JWT的请求可访问。

我们将从第一个需求开始:验证用户。然后我们将通过发行JWT来扩展它。最后,我们将创建一个受保护的路由,用于检查请求上的有效JWT。

首先,我们需要安装所需的软件包。Passport提供了一种名为Passport-local的策略,它实现了一种用户名/密码身份验证机制,这符合我们在这一部分用例中的需求。

$ npm install --save @nestjs/passport passport passport-local
$ npm install --save-dev @types/passport-local

对于您选择的任何Passport策略,都需要@nestjs/Passport和Passport包。然后,需要安装特定策略的包(例如,passport-jwt或passport-local),它实现您正在构建的特定身份验证策略。此外,您还可以安装任何Passport策略的类型定义,如上面的@types/Passport-local所示,它在编写TypeScript代码时提供了帮助。

Passport 策略

现在可以实现身份认证功能了。我们将首先概述用于任何Passport策略的流程。将Passport本身看作一个框架是有帮助的。框架的优雅之处在于,它将身份验证过程抽象为几个基本步骤,您可以根据实现的策略对这些步骤进行自定义。它类似于一个框架,因为您可以通过提供定制参数(作为JSON对象)和回调函数(Passport在适当的时候调用这些回调函数)的形式来配置它。@nestjs/passport模块将该框架包装在一个Nest风格的包中,使其易于集成到Nest应用程序中。下面我们将使用@nestjs/passport,但首先让我们考虑一下vanilla Passport是如何工作的。

在vanilla Passport中,您可以通过提供以下两项配置策略:

  1. 组特定于该策略的选项。例如,在JWT策略中,您可以提供一个秘令来对令牌进行签名。
  2. "验证回调",在这里您可以告诉Passport如何与您的用户存储交互(在这里您可以管理用户帐户)。在这里,验证用户是否存在(或创建一个新用户),以及他们的凭据是否有效。Passport库期望这个回调在验证成功时返回完整的用户消息,在验证失败时返回null(失败定义为用户没有找到,或者在使用Passport-local的情况下,密码不匹配)。

使用@nestjs/passport,您可以通过扩展PassportStrategy类来配置passport策略。通过调用子类中的super()方法传递策略选项(上面第1项),可以选择传递一个options对象。通过在子类中实现validate()方法,可以提供verify回调(上面第2项)。

我们将从生成一个AuthModule开始,其中有一个AuthService:

$ nest g module auth
$ nest g service auth

当我们实现AuthService时,我们会发现在UsersService中封装用户操作是很有用的,所以现在让我们生成这个模块和服务:

$ nest g module users
$ nest g service users

替换这些生成文件的默认内容,如下所示。对于我们的示例应用程序,UsersService只是在内存中维护一个硬编码的用户列表,以及一个根据用户名检索用户列表的find方法。在真正的应用程序中,这是您使用选择的库(例如TypeORM、Sequelize、Mongoose等)构建用户模型和持久层。

users/users.service.ts

import { Injectable } from '@nestjs/common';

export type User = any;

@Injectable()
export class UsersService {
  private readonly users: User[];

  constructor() {
    this.users = [
      {
        userId: 1,
        username: 'john',
        password: 'changeme',
      },
      {
        userId: 2,
        username: 'chris',
        password: 'secret',
      },
      {
        userId: 3,
        username: 'maria',
        password: 'guess',
      },
    ];
  }

  async findOne(username: string): Promise<User | undefined> {
    return this.users.find(user => user.username === username);
  }
}

在UsersModule中,惟一需要做的更改是将UsersService添加到@Module装饰器的exports数组中,以便提供给其他模块外部可见(我们很快将在AuthService中使用它)。

users/users.module.ts

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';

@Module({
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

我们的AuthService的任务是检索用户并验证密码。为此,我们创建了validateUser()方法。在下面的代码中,我们使用ES6扩展操作符从user对象中提取password属性,然后再返回它。稍后,我们将从Passport本地策略中调用validateUser()方法。

auth/auth.service.ts

import { Injectable } from '@nestjs/common';
import { UsersService } from '../users/users.service';

@Injectable()
export class AuthService {
  constructor(private readonly usersService: UsersService) {}

  async validateUser(username: string, pass: string): Promise<any> {
    const user = await this.usersService.findOne(username);
    if (user && user.password === pass) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }
}

?> 当然,在实际的应用程序中,您不会以纯文本形式存储密码。 取而代之的是使用带有加密单向哈希算法的bcrypt之类的库。使用这种方法,您只需存储散列密码,然后将存储的密码与输入密码的散列版本进行比较,这样就不会以纯文本的形式存储或暴露用户密码。为了保持我们的示例应用程序的简单性,我们违反了这个绝对命令并使用纯文本。不要在真正的应用程序中这样做!

现在,我们更新AuthModule来导入UsersModule。

auth/auth.module.ts

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';

@Module({
  imports: [UsersModule],
  providers: [AuthService],
})
export class AuthModule {}

现在我们可以实现Passport本地身份验证策略。在auth文件夹中创建一个名为local.strategy.ts文件,并添加以下代码:

auth/local.strategy.ts

import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly authService: AuthService) {
    super();
  }

  async validate(username: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(username, password);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}

我们遵循了前面描述的所有护照策略。在我们的passport-local用例中,没有配置选项,因此我们的构造函数只是调用super(),没有options对象。

我们还实现了validate()方法。对于每个策略,Passport将使用适当的特定于策略的一组参数调用verify函数(使用@nestjs/Passport中的validate()方法实现)。对于本地策略,Passport需要一个具有以下签名的validate()方法:validate(username: string, password: string): any。

大多数验证工作是在我们的AuthService中完成的(在UserService的帮助下),所以这个方法非常简单。任何Passport策略的validate()方法都将遵循类似的模式,只是表示凭证的细节方面有所不同。如果找到了用户并且凭据有效,则返回该用户,以便Passport能够完成其任务(例如,在请求对象上创建user属性),并且请求处理管道可以继续。如果没有找到,我们抛出一个异常,让异常层处理它。

通常,每种策略的validate()方法的惟一显著差异是如何确定用户是否存在和是否有效。例如,在JWT策略中,根据需求,我们可以评估解码令牌中携带的userId是否与用户数据库中的记录匹配,或者是否与已撤销的令牌列表匹配。因此,这种子类化和实现特定于策略验证的模式是一致的、优雅的和可扩展的。

我们需要配置AuthModule来使用刚才定义的Passport特性。更新auth.module。看起来像这样:

auth/auth.module.ts

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '@nestjs/passport';
import { LocalStrategy } from './local.strategy';

@Module({
  imports: [UsersModule, PassportModule],
  providers: [AuthService, LocalStrategy],
})
export class AuthModule {}

内置 Passport 守卫

守卫章节描述了守卫的主要功能:确定请求是否由路由处理程序。这仍然是正确的,我们将很快使用这个标准功能。但是,在使用@nestjs/passport模块的情况下,我们还将引入一个新的小问题,这个问题一开始可能会让人感到困惑,现在让我们来讨论一下。从身份验证的角度来看,您的应用程序可以以两种状态存在:

  1. 用户/客户端未登录(未通过身份验证)
  2. 用户/客户端已登录(已通过身份验证)

在第一种情况下(用户没有登录),我们需要执行两个不同的功能:

  • 限制未经身份验证的用户可以访问的路由(即拒绝访问受限制的路由)。 我们将使用熟悉的警卫来处理这个功能,方法是在受保护的路由上放置一个警卫。我们将在这个守卫中检查是否存在有效的JWT,所以我们稍后将在成功发出JWT之后处理这个守卫。
  • 当以前未经身份验证的用户尝试登录时,启动身份验证步骤。这时我们向有效用户发出JWT的步骤。考虑一下这个问题,我们知道需要POST用户名/密码凭证来启动身份验证,所以我们将设置POST/auth/login路径来处理这个问题。这就提出了一个问题:在这条路由上,我们究竟如何实施“护照-本地”战略?

答案很简单:使用另一种稍微不同类型的守卫。@nestjs/passport模块为我们提供了一个内置的守卫,可以完成这一任务。这个保护调用Passport策略并启动上面描述的步骤(检索凭证、运行verify函数、创建用户属性等)。

上面列举的第二种情况(登录用户)仅仅依赖于我们已经讨论过的标准类型的守卫,以便为登录用户启用对受保护路由的访问。

登录路由

有了这个策略,我们现在就可以实现一个简单的/auth/login路由,并应用内置的守卫来启动护照本地流。 打开app.controller.ts文件,并将其内容替换为以下内容:

app.controller.ts

import { Controller, Request, Post, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Controller()
export class AppController {
  @UseGuards(AuthGuard('local'))
  @Post('auth/login')
  async login(@Request() req) {
    return req.user;
  }
}

对于@useguard(AuthGuard('local')),我们使用的是一个AuthGuard,它是在我们扩展护照-本地策略时@nestjs/passportautomatic为我们准备的。我们来分析一下。我们的Passport本地策略默认名为"local"。我们在@UseGuards()装饰器中引用这个名称,以便将它与护照本地包提供的代码关联起来。这用于消除在应用程序中有多个Passport策略时调用哪个策略的歧义(每个策略可能提供一个特定于策略的AuthGuard)。虽然到目前为止我们只有一个这样的策略,但我们很快就会添加第二个,所以这是消除歧义所需要的。

为了测试我们的路由,我们将/auth/login路由简单地返回用户。这还允许我们演示另一个Passport特性:Passport根据从validate()方法返回的值自动创建一个user对象,并将其作为req.user分配给请求对象。稍后,我们将用创建并返回JWT的代码替换它。

因为这些是API路由,所以我们将使用常用的cURL库来测试它们。您可以使用UsersService中硬编码的任何用户对象进行测试。

$ # POST to /auth/login
$ curl -X POST http://localhost:3000/auth/login -d '{"username": "john", "password": "changeme"}' -H "Content-Type: application/json"
$ # result -> {"userId":1,"username":"john"}

JWT 功能

我们已经准备好进入JWT部分的认证系统。让我们回顾并完善我们的需求:

  • 允许用户使用用户名/密码进行身份验证,返回JWT以便在后续调用受保护的API端点时使用。我们正在努力满足这一要求。为了完成它,我们需要编写发出JWT的代码。
  • 创建基于token的有效JWT的存在而受保护的API路由。

我们需要安装更多的包来支持我们的JWT需求:

$ npm install @nestjs/jwt passport-jwt
$ npm install @types/passport-jwt --save-dev

@nest/jwt包是一个实用程序包,可以帮助jwt操作。passport-jwt包是实现JWT策略的Passport包,@types/passport-jwt提供TypeScript类型定义。

让我们仔细看看如何处理POST/auth/login请求。我们使用护照本地策略提供的内置AuthGuard来装饰路由。这意味着:

  1. 只有在验证了用户之后,才会调用路由处理程序
  2. req参数将包含一个用户属性(在passport-local 身份验证流期间由Passport填充)

考虑到这一点,我们现在终于可以生成一个真正的JWT,并以这种方式返回它。为了使我们的服务保持干净的模块化,我们将在authService中生成JWT。在auth文件夹中添加auth.service.ts文件,并添加login()方法,导入JwtService,如下图所示:

auth/auth.service.ts

import { Injectable } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import { JwtService } from '@nestjs/jwt';

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

  async validateUser(username: string, pass: string): Promise<any> {
    const user = await this.usersService.findOne(username);
    if (user && user.password === pass) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }

  async login(user: any) {
    const payload = { username: user.username, sub: user.userId };
    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}

我们使用@nestjs/jwt库,该库提供了一个sign()函数,用于从用户对象属性的子集生成jwt,然后以简单对象的形式返回一个access_token属性。注意:我们选择sub的属性名来保持我们的userId值与JWT标准一致。不要忘记将JwtService提供者注入到AuthService中。

现在,我们需要更新AuthModule来导入新的依赖项并配置JwtModule。

首先,在auth文件夹下创建auth/constants.ts,并添加以下代码:

auth/constants.ts

export const jwtConstants = {
  secret: 'secretKey',
};

我们将使用它在JWT签名和验证步骤之间共享密钥。

不要公开公开此密钥。我们在这里这样做是为了清楚地说明代码在做什么,但是在生产系统中,您必须使用适当的措施来保护这个密钥,比如机密库、环境变量或配置服务。

现在,在auth文件夹下auth.module.ts,并更新它看起来像这样:

auth/auth.module.tsJS

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from './constants';

@Module({
  imports: [
    UsersModule,
    PassportModule,
    JwtModule.register({
      secret: jwtConstants.secret,
      signOptions: { expiresIn: '60s' },
    }),
  ],
  providers: [AuthService, LocalStrategy],
  exports: [AuthService],
})
export class AuthModule {}

我们使用register()配置JwtModule,并传入一个配置对象。有关Nest JwtModule的更多信息请参见此处,有关可用配置选项的更多信息请参见此处

现在我们可以更新/auth/login路径来返回JWT。

app.controller.ts

import { Controller, Request, Post, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth/auth.service';

@Controller()
export class AppController {
  constructor(private readonly authService: AuthService) {}

  @UseGuards(AuthGuard('local'))
  @Post('auth/login')
  async login(@Request() req) {
    return this.authService.login(req.user);
  }
}

让我们继续使用cURL测试我们的路由。您可以使用UsersService中硬编码的任何用户对象进行测试。

$ # POST to /auth/login
$ curl -X POST http://localhost:3000/auth/login -d '{"username": "john", "password": "changeme"}' -H "Content-Type: application/json"
$ # result -> {"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
$ # Note: above JWT truncated

实施 Passport JWT

我们现在可以处理我们的最终需求:通过要求在请求时提供有效的JWT来保护端点。护照对我们也有帮助。它提供了用于用JSON Web标记保护RESTful端点的passport-jwt策略。在auth文件夹中jwt.strategy.ts,并添加以下代码:

auth/jwt.strategy.ts

import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { jwtConstants } from './constants';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: jwtConstants.secret,
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}

对于我们的JwtStrategy,我们遵循了前面描述的所有Passport策略的相同配方。这个策略需要一些初始化,因此我们通过在super()调用中传递一个options对象来实现。您可以在这里阅读关于可用选项的更多信息。在我们的例子中,这些选项是:

  • jwtFromRequest:提供从请求中提取JWT的方法。我们将使用在API请求的授权头中提供token的标准方法。这里描述了其他选项。

ignoreExpiration:为了明确起见,我们选择默认的false设置,它将确保JWT没有过期的责任委托给Passport模块。这意味着,如果我们的路由提供了一个过期的JWT,请求将被拒绝,并发送401未经授权的响应。护照会自动为我们办理。

secret orkey:我们使用权宜的选项来提供对称的秘密来签署令牌。其他选项,如pemo编码的公钥,可能更适合于生产应用程序(有关更多信息,请参见此处)。如前所述,无论如何,不要把这个秘密公开。

validate()方法值得讨论一下。对于JWT策略,Passport首先验证JWT的签名并解码JSON。然后调用我们的validate()方法,该方法将解码后的JSON作为其单个参数传递。根据JWT签名的工作方式,我们可以保证接收到之前已签名并发给有效用户的有效token令牌。

因此,我们对validate()回调的响应很简单:我们只是返回一个包含userId和username属性的对象。再次回忆一下,Passport将基于validate()方法的返回值构建一个user对象,并将其作为属性附加到请求对象上。

同样值得指出的是,这种方法为我们留出了将其他业务逻辑注入流程的空间(就像"挂钩"一样)。例如,我们可以在validate()方法中执行数据库查询,以提取关于用户的更多信息,从而在请求中提供更丰富的用户对象。这也是我们决定进行进一步令牌验证的地方,例如在已撤销的令牌列表中查找userId,使我们能够执行令牌撤销。我们在示例代码中实现的模型是一个快速的"无状态JWT"模型,其中根据有效JWT的存在立即对每个API调用进行授权,并在请求管道中提供关于请求者(其userid和username)的少量信息。

在AuthModule中添加新的JwtStrategy作为提供者:

auth/auth.module.ts

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';
import { JwtStrategy } from './jwt.strategy';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from './constants';

@Module({
  imports: [
    UsersModule,
    PassportModule,
    JwtModule.register({
      secret: jwtConstants.secret,
      signOptions: { expiresIn: '60s' },
    }),
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy],
  exports: [AuthService],
})
export class AuthModule {}

通过导入JWT签名时使用的相同密钥,我们可以确保Passport执行的验证阶段和AuthService执行的签名阶段使用公共密钥。

实现受保护的路由和JWT策略保护,我们现在可以实现受保护的路由及其相关的保护。

打开app.controller.ts文件,更新如下:

app.controller.ts

import { Controller, Get, Request, Post, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth/auth.service';

@Controller()
export class AppController {
  constructor(private readonly authService: AuthService) {}

  @UseGuards(AuthGuard('local'))
  @Post('auth/login')
  async login(@Request() req) {
    return this.authService.login(req.user);
  }

  @UseGuards(AuthGuard('jwt'))
  @Get('profile')
  getProfile(@Request() req) {
    return req.user;
  }
}

同样,我们将应用在配置passport-jwt模块时@nestjs/passport模块自动为我们提供的AuthGuard。这个保护由它的默认名称jwt引用。当我们请求GET /profile路由时,保护程序将自动调用我们的passport-jwt自定义配置逻辑,验证JWT,并将用户属性分配给请求对象。

确保应用程序正在运行,并使用cURL测试路由。

$ # GET /profile
$ curl http://localhost:3000/profile
$ # result -> {"statusCode":401,"error":"Unauthorized"}

$ # POST /auth/login
$ curl -X POST http://localhost:3000/auth/login -d '{"username": "john", "password": "changeme"}' -H "Content-Type: application/json"
$ # result -> {"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2Vybm... }

$ # GET /profile using access_token returned from previous step as bearer code
$ curl http://localhost:3000/profile -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2Vybm..."
$ # result -> {"userId":1,"username":"john"}

注意,在AuthModule中,我们将JWT配置为60秒过期。这个过期时间可能太短了,而处理令牌过期和刷新的细节超出了本文的范围。然而,我们选择它来展示JWT的一个重要品质和jwt护照战略。如果您在验证之后等待60秒再尝试GET /profile请求,您将收到401未授权响应。这是因为Passport会自动检查JWT的过期时间,从而省去了在应用程序中这样做的麻烦。

我们现在已经完成了JWT身份验证实现。JavaScript客户端(如Angular/React/Vue)和其他JavaScript应用程序现在可以安全地与我们的API服务器进行身份验证和通信。

默认策略

在我们的AppController中,我们在@AuthGuard()装饰器中传递策略的名称。我们需要这样做,因为我们已经介绍了两种Passport策略(护照本地策略和护照jwt策略),这两种策略都提供了各种Passport组件的实现。传递名称可以消除我们链接到的实现的歧义。当应用程序中包含多个策略时,我们可以声明一个默认策略,这样如果使用该默认策略,我们就不必在@AuthGuard装饰器中传递名称。下面介绍如何在导入PassportModule时注册默认策略。这段代码将进入AuthModule:

要确定默认策略行为,您可以注册PassportModule。

auth.module.ts

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from './constants';
import { JwtStrategy } from './jwt.strategy';

@Module({
  imports: [
    PassportModule.register({ defaultStrategy: 'jwt' }),
    JwtModule.register({
      secret: jwtConstants.secret,
      signOptions: { expiresIn: '60s' },
    }),
    UsersModule
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy],
  exports: [AuthService],
})
export class AuthModule {}

自定义 Passport

根据所使用的策略,护照会采用一系列影响库行为的属性。使用register()方法将选项对象直接传递给护照实例。

PassportModule.register({ session: true });

您还可以在策略的构造函数中传递一个options对象来配置它们。至于本地策略,你可以通过例如:

constructor(private readonly authService: AuthService) {
  super({
    usernameField: 'email',
    passwordField: 'password',
  });
}

看看Passport Website官方文档吧。

命名策略

在实现策略时,可以通过向PassportStrategy函数传递第二个参数来为其提供名称。如果你不这样做,每个战略将有一个默认的名称(例如,"jwt"的jwt策略 ):

export class JwtStrategy extends PassportStrategy(Strategy, 'myjwt')

然后,通过一个像@AuthGuard('myjwt')这样的装饰器来引用它。

GraphQL

为了使用带有GraphQL的AuthGuard,扩展内置的AuthGuard类并覆盖getRequest()方法。

@Injectable()
export class GqlAuthGuard extends AuthGuard('jwt') {
  getRequest(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    return ctx.getContext().req;
  }
}

要使用上述结构,请确保在GraphQL模块设置中将request (req)对象作为上下文值的一部分传递:

GraphQLModule.forRoot({
  context: ({ req }) => ({ req }),
});

要在graphql解析器中获得当前经过身份验证的用户,可以定义一个用户装饰器:

import { createParamDecorator } from '@nestjs/common';
export const CurrentUser = createParamDecorator(
  (data, [root, args, ctx, info]) => ctx.req.user,
);

要在解析器中使用上述装饰器,请确保将其作为查询的参数:

@Query(returns => User)
@UseGuards(GqlAuthGuard)
whoAmI(@CurrentUser() user: User) {
  return this.userService.findById(user.id);
}