動態

詳情 返回 返回

NestJS 中 JWT 認證的最佳實踐:利用 .env 文件和 ConfigService - 動態 詳情

要在 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 進行身份驗證。

Add a new 評論

Some HTML is okay.