在本章中,我们将学习如何在 NestJS 应用程序中实现鉴权和认证。鉴权是验证用户是否有权限访问某些资源或执行某些操作的过程,而认证是确认用户的身份信息是否有效的过程。为了实现鉴权和认证,我们将使用 Passport.js,它是一个非常流行的 Node.js 身份验证中间件。
# 5.1 使用 Passport.js 进行身份验证
首先,我们需要安装 Passport.js 和相关的身份验证策略。
npm install @nestjs/passport passport passport-local
- @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;
}
}
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;
}
}
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 {}
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;
}
}
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);
}
}
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);
}
}
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 };
}
}
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 {}
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';
}
}
2
3
4
5
6
7
8
9
10
11
在上面的代码中,我们创建了一个名为 CatsController 的控制器,并使用 JwtAuthGuard 路由守卫来保护 findAll 方法。这样,只有带有有效 JWT 的请求才能访问该路由。