头图

要在 NestJS 中配置 Passport 的 Local 策略,需要完成以下步骤。这包括创建策略、守卫和服务以处理用户验证。

1. 安装依赖包

首先,确保已经安装必要的依赖包:

npm install @nestjs/passport passport passport-local

2. 创建 .env 文件

在项目根目录下创建一个 .env 文件,并添加 JWT 相关的配置:

JWT_SECRET=your_jwt_secret_key
JWT_EXPIRES_IN=3600s

3. 配置 ConfigModule

在 app.module.ts 中导入并配置 ConfigModule:

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AuthModule } from './auth/auth.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // 设置为全局模块
    }),
    AuthModule,
  ],
})
export class AppModule {}

4. 创建 Auth 模块和服务

如果你还没有创建 auth 模块和服务,可以使用 Nest CLI 创建它们:

nest generate module auth
nest generate service auth
nest generate controller auth

5. 配置 JWT 模块

auth.module.ts 中导入并配置 JWT 模块,确保从 .env 文件中获取配置:

导入和配置 Local, Jwt 策略和服务

// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { LocalStrategy } from './local.strategy';
import { JwtStrategy } from './jwt.strategy';

@Module({
  imports: [
    PassportModule,
    JwtModule.registerAsync({
            // 确保 ConfigModule 被导入,这样 ConfigService 可以在 useFactory 中使用。
            // imports: [ConfigModule],  // 如果在app.moudle.ts导入,且设置全局,此处可以省略
            
            // 注入 ConfigService,这样它可以在 useFactory 中使用。
            inject: [ConfigService],
            
            useFactory: async (configService: ConfigService) => ({
                secret: configService.get<string>('JWT_SECRET'),
                signOptions: { expiresIn: configService.get<string>('JWT_EXPIRES_IN') },
            }),
        }),
        
        // 导入 ConfigModule 以便 AuthService 中可以使用 ConfigService, 
        // ConfigModule // 如果在app.moudle.ts导入,且设置全局,此处可以省略
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy],
  controllers: [AuthController],
})
export class AuthModule {}

6. 创建 JWT 策略

创建一个 JwtStrategy 类来验证 JWT 令牌:

// src/auth/jwt.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(configService: ConfigService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: configService.get<string>('JWT_SECRET'),
    });
  }

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

7. 创建 JwtAuthGuard 守卫

创建一个 JwtAuthGuard 类来保护需要进行 JWT 身份验证的路由:

// src/auth/jwt-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

8. 创建 Local 策略

创建一个 LocalStrategy 类,它将负责用户验证逻辑。放在 auth 目录下:

// src/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 authService: AuthService) {
      /**
       * usernameField: 指定用户名字段的名称,默认为 'username'。
       * passwordField: 指定密码字段的名称,默认为 'password'。
       * passReqToCallback: 如果设置为 true,request 对象将作为第一个参数传递给验证函数。
       * 
       * 如果你的请求中的用户名字段不是默认的 username,而是 account 或其他名称,可以使用这个参数指定。
       */
    super({ usernameField: 'account' });
  }

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

9. 创建 Local Auth 守卫

创建一个 LocalAuthGuard 类来保护需要进行本地身份验证的路由:

// src/auth/local-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}

10. 在 Auth 服务中实现用户验证逻辑

auth.service.ts 中添加验证用户的逻辑。这个服务将从数据库或其他用户存储中查找用户。

// src/auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

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

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

  async validateUser(username: string, pass: string): Promise<any> {
    // 假设有一个用户验证逻辑
    const user = { userId: 1, username: 'test', password: 'test' }; // 这是一个示例用户
    if (user && user.password === pass) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }
}

11. 创建 Auth 控制器

创建一个控制器来处理登录请求,并使用 LocalAuthGuard 进行保护:

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

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

  @UseGuards(LocalAuthGuard)
  @Post('login')
  async login(@Request() req) {
    return this.authService.login(req.user);
  }
}

12. 保护路由

在需要保护的控制器中使用 JwtAuthGuard

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

@Controller('protected')
export class ProtectedController {
  @UseGuards(JwtAuthGuard)
  @Get()
  getProtectedResource() {
    return { message: 'This is a protected resource' };
  }
}

完整示例结构

src/
  auth/
    auth.module.ts
    auth.service.ts
    auth.controller.ts
    local.strategy.ts
    local-auth.guard.ts
    jwt.strategy.ts
    jwt-auth.guard.ts
  protected/
    protected.controller.ts

测试流程

  1. 启动 NestJS 应用程序。
  2. 使用 Postman 或类似工具发送 POST 请求到 /auth/login 端点,提供正确的用户名和密码。
  3. 获取 JWT 令牌后,在请求受保护资源时在请求头中添加 Authorization: Bearer <token>

这样,你就成功地在 NestJS 中配置了 PassportLocal 策略,并使用 JWT 进行身份验证。


梁三石
34 声望8 粉丝