Nest基本概念 - 鉴权和认证

7/13/2023 NodejsNestjs

在本章中,我们将学习如何在 NestJS 应用程序中实现鉴权和认证。鉴权是验证用户是否有权限访问某些资源或执行某些操作的过程,而认证是确认用户的身份信息是否有效的过程。为了实现鉴权和认证,我们将使用 Passport.js,它是一个非常流行的 Node.js 身份验证中间件。

# 5.1 使用 Passport.js 进行身份验证

首先,我们需要安装 Passport.js 和相关的身份验证策略。

npm install @nestjs/passport passport passport-local
1
  • @nestjs/passport:NestJS 提供的 Passport.js 模块。
  • passport:Passport.js 库本身。
  • passport-local:Passport.js 的本地策略,用于基于用户名和密码进行身份验证。

# 5.2 配置不同的身份验证策略

NestJS 使用 Passport.js 的策略来实现不同的身份验证方式。我们可以配置多种策略,例如基于用户名和密码的本地策略、基于 JWT 的策略等。

# 配置本地策略

在本地策略中,我们将使用用户名和密码进行身份验证。首先,我们需要创建一个本地策略并将其添加到应用程序中。

在 auth 文件夹下创建一个新的文件 local.strategy.ts,并添加以下代码:

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

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super({
      usernameField: 'username', // 请求体中用户名字段的名称
      passwordField: 'password', // 请求体中密码字段的名称
    });
  }

  async validate(username: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(username, password);
    if (!user) {
      throw new Error('Invalid credentials');
    }
    return user;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

在上面的代码中,我们创建了一个名为 LocalStrategy 的本地策略,并继承自 PassportStrategy 类。在构造函数中,我们使用 super() 方法来配置本地策略,并指定了请求体中用户名和密码字段的名称。

validate 方法是本地策略的验证逻辑。在这个例子中,我们通过 AuthService 的 validateUser 方法来验证用户名和密码,如果验证成功,则返回用户信息,否则抛出异常。

# 创建 Auth Service

在使用本地策略进行身份验证时,我们需要一个 AuthService 来处理用户的验证逻辑。

在 auth 文件夹下创建一个新的文件 auth.service.ts,并添加以下代码:

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

@Injectable()
export class AuthService {
  // 在实际应用中,我们可以通过数据库或其他方式来验证用户的身份
  async validateUser(username: string, password: string): Promise<any> {
    const user = { id: 1, username: 'admin', password: 'password' }; // 假设这是从数据库中查询得到的用户信息
    if (user && user.password === password) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在上面的代码中,我们创建了一个名为 AuthService 的服务,并实现了 validateUser 方法。在实际应用中,我们可以通过数据库或其他方式来验证用户的身份,这里我们假设返回的用户信息是从数据库中查询得到的。

# 5.3 创建 Auth Module

现在,我们已经配置了本地策略和 Auth Service,接下来需要将它们添加到一个 Auth Module 中。

在 auth 文件夹下创建一个新的文件 auth.module.ts,并添加以下代码:

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

@Module({
  imports: [
    PassportModule, // 导入 PassportModule,使其可用于应用程序
  ],
  providers: [AuthService, LocalStrategy], // 注册 AuthService 和 LocalStrategy
  exports: [AuthService], // 导出 AuthService,以便其他模块也可以使用它
})
export class AuthModule {}
1
2
3
4
5
6
7
8
9
10
11
12
13

在上面的代码中,我们创建了一个名为 AuthModule 的模块,并使用 @Module() 装饰器进行配置。在 imports 中导入 PassportModule,使其可用于应用程序。在 providers 中注册 AuthService 和 LocalStrategy,以便其他模块可以使用它们。最后,我们通过 exports 导出 AuthService,以便其他模块也可以使用它。

# 5.4 创建 Auth Controller

现在我们已经配置了 Auth Module,接下来需要创建一个 Auth Controller 来处理用户身份验证的请求。

在 auth 文件夹下创建一个新的文件 auth.controller.ts,并添加以下代码:

import { Controller, Post, Request, UseGuards } from '@nestjs/common';
import { LocalAuthGuard } from './local-auth.guard';
import { AuthService } from './auth.service';

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

  @Post('login')
  @UseGuards(LocalAuthGuard)
  async login(@Request() req: any): Promise<any> {
    // 在这里可以返回 JWT 或其他用户信息
    return req.user;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

在上面的代码中,我们创建了一个名为 AuthController 的控制器,并使用 @Controller() 装饰器进行配置。在构造函数中注入了 AuthService。

我们创建了一个名为 login 的路由,并使用 @Post('login') 装饰器来指定这是一个 POST 请求处理。我们还使用 @UseGuards(LocalAuthGuard) 装饰器来应用 LocalAuthGuard,它是一个自定义的路由守卫,用于保护登录路由

# 5.5 创建自定义的路由守卫

在 NestJS 中,路由守卫用于保护路由,它可以在请求到达控制器之前或之后执行一些操作。我们可以创建自定义的路由守卫来实现身份验证、权限检查等功能。

在 auth 文件夹下创建一个新的文件 local-auth.guard.ts,并添加以下代码:

import { Injectable, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {
  canActivate(context: ExecutionContext) {
    // 在这里添加额外的身份验证逻辑,如果验证失败则返回 false,否则返回 true
    return super.canActivate(context);
  }
}
1
2
3
4
5
6
7
8
9
10

在上面的代码中,我们创建了一个名为 LocalAuthGuard 的自定义路由守卫,并继承自 AuthGuard 类。我们传递 'local' 参数给 AuthGuard 的构造函数,这告诉它使用我们之前配置的本地策略。

我们重写了 canActivate 方法,可以在这里添加额外的身份验证逻辑。例如,我们可以检查请求头中是否包含特定的认证信息、检查请求体中是否携带有效的令牌等。如果验证失败,则返回 false,否则返回 true。

# 5.6 使用路由守卫和认证服务

现在我们已经创建了 LocalAuthGuard 自定义路由守卫,并在 AuthController 中应用了它。接下来,我们需要在控制器中使用 AuthService 来实现登录功能。

在 auth.controller.ts 文件中,我们已经有一个 login 方法,现在我们可以修改它来实现登录功能:

import { Controller, Post, Request, UseGuards } from '@nestjs/common';
import { LocalAuthGuard } from './local-auth.guard';
import { AuthService } from './auth.service';

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

  @Post('login')
  @UseGuards(LocalAuthGuard)
  async login(@Request() req: any): Promise<any> {
    return this.authService.login(req.user);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

在上面的代码中,我们修改了 login 方法,使用 this.authService.login(req.user) 来实现登录功能。我们假设 req.user 包含了经过验证的用户信息,这是在 LocalStrategy 的 validate 方法中返回的。

# 5.7 创建 JWT 策略

除了本地策略,我们还可以实现基于 JSON Web Token(JWT)的身份验证策略。

在 auth 文件夹下创建一个新的文件 jwt.strategy.ts,并添加以下代码:

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

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), // 从请求头中提取 JWT
      secretOrKey: 'your_secret_key', // 用于验证签名的密钥,根据实际情况进行配置
    });
  }

  async validate(payload: any): Promise<any> {
    // 在这里可以根据 payload 中的信息来验证用户身份
    // 例如,根据 payload 中的用户 ID 查询数据库,并返回对应的用户信息
    return { userId: payload.sub, username: payload.username };
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

在上面的代码中,我们创建了一个名为 JwtStrategy 的 JWT 策略,并继承自 PassportStrategy 类。在构造函数中,我们使用 super() 方法来配置 JWT 策略,并指定了从请求头中提取 JWT,并使用 your_secret_key 作为验证签名的密钥。

validate 方法是 JWT 策略的验证逻辑。在这个例子中,我们简单地根据 payload 中的信息返回一个包含 userId 和 username 的对象。在实际应用中,我们可以根据 payload 中的信息来验证用户身份,例如根据用户 ID 查询数据库并返回对应的用户信息。

# 5.8 注册 JWT 策略

我们已经创建了 JwtStrategy JWT 策略,现在需要将它添加到 Auth Module 中。

在 auth.module.ts 文件中,我们需要导入并注册 JwtStrategy:

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';
import { JwtStrategy } from './jwt.strategy'; // 导入 JwtStrategy
import { PassportModule } from '@nestjs/passport';

@Module({
  imports: [
    PassportModule,
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy], // 注册 JwtStrategy
  exports: [AuthService],
})

export class AuthModule {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

在上面的代码中,我们导入了 JwtStrategy 并在 providers 中注册它,以便它可以在整个 Auth Module 中使用。

# 5.9 使用 JWT 策略进行认证

现在,我们已经注册了 JwtStrategy,我们可以在应用程序的其他地方使用它进行认证。

例如,我们可以在其他控制器中应用 JwtAuthGuard 路由守卫来保护路由:

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

@Controller('cats')
export class CatsController {
  @Get()
  @UseGuards(JwtAuthGuard)
  findAll(): string {
    return 'This action returns all cats';
  }
}
1
2
3
4
5
6
7
8
9
10
11

在上面的代码中,我们创建了一个名为 CatsController 的控制器,并使用 JwtAuthGuard 路由守卫来保护 findAll 方法。这样,只有带有有效 JWT 的请求才能访问该路由。

编辑时间: 7/13/2023, 10:00:00 AM