Nest.js 框架认证与授权

class Nest.js,Jwt

在现代 Web 应用中,认证与授权是确保安全性的关键部分。Nest.js 提供了强大的工具来实现这些功能,尤其是通过 JWT(JSON Web Token)认证机制、Guards 和策略。本文将详细介绍如何在 Nest.js 中实现 JWT 认证与授权,并结合具体示例进行讲解。

1. JWT 认证机制概述

JWT 是一种开放标准(RFC 7519),用于在各方之间以 JSON 对象的方式安全地传输信息。JWT 由三部分组成:头部(Header)、有效载荷(Payload)和签名(Signature)。通过 JWT,用户可以在登录后获得一个令牌,该令牌在后续请求中作为身份凭证。

2. 安装相关依赖

首先,我们需要安装一些必要的依赖,包括 @nestjs/jwtpassport 相关的包。

npm install @nestjs/passport passport passport-jwt @nestjs/jwt bcrypt

3. 创建认证模块

3.1 创建用户实体

我们首先需要一个用户实体,用于数据库存储用户信息。在 users 文件夹中创建 user.entity.ts

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  password: string;
}

3.2 创建用户服务

users.service.ts 中实现用户注册和查找功能:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import * as bcrypt from 'bcrypt';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
  ) {}

  async create(username: string, password: string): Promise<User> {
    const hashedPassword = await bcrypt.hash(password, 10);
    const user = this.usersRepository.create({ username, password: hashedPassword });
    return this.usersRepository.save(user);
  }

  async findOne(username: string): Promise<User | undefined> {
    return this.usersRepository.findOneBy({ username });
  }
}

3.3 创建认证模块

接下来,创建一个认证模块 auth.module.ts

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './jwt.strategy';

@Module({
  imports: [
    UsersModule,
    JwtModule.register({
      secret: 'your_secret_key', // 应用的密钥,生产中应放在环境变量中
      signOptions: { expiresIn: '60s' }, // 令牌过期时间
    }),
  ],
  providers: [AuthService, JwtStrategy],
  controllers: [AuthController],
})
export class AuthModule {}

3.4 创建认证服务

auth.service.ts 中实现用户登录和 JWT 生成:

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

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

  async validateUser(username: string, password: string): Promise<any> {
    const user = await this.usersService.findOne(username);
    if (user && (await bcrypt.compare(password, user.password))) {
      const { password, ...result } = user; // 去掉密码
      return result;
    }
    return null;
  }

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

3.5 创建认证控制器

auth.controller.ts 中定义登录接口:

import { Controller, Post, Body } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersService } from '../users/users.service';

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

  @Post('register')
  async register(@Body() body: { username: string; password: string }) {
    return this.usersService.create(body.username, body.password);
  }

  @Post('login')
  async login(@Body() body: { username: string; password: string }) {
    const user = await this.authService.validateUser(body.username, body.password);
    if (!user) {
      throw new Error('Invalid credentials');
    }
    return this.authService.login(user);
  }
}

4. Guards 与策略

Guards 用于实现请求的授权逻辑,可以通过实现 CanActivate 接口来定义。策略用于验证用户身份。

4.1 创建 JWT 策略

jwt.strategy.ts 中定义 JWT 策略:

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy, ExtractJwt } from 'passport-jwt';
import { UsersService } from '../users/users.service';
import { User } from '../users/user.entity';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(private usersService: UsersService) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: 'your_secret_key', // 与 JwtModule 中的密钥相同
    });
  }

  async validate(payload: any): Promise<User> {
    return this.usersService.findOne(payload.username);
  }
}

4.2 创建 Auth Guard

Nest.js 提供了 @UseGuards() 装饰器来使用 Guard。可以在控制器中对特定路由使用。

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

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  canActivate(context: ExecutionContext) {
    return super.canActivate(context);
  }
}

4.3 使用 Guard

在需要保护的控制器中使用 JWT Guard:

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

@Controller('profile')
export class ProfileController {
  @UseGuards(JwtAuthGuard)
  @Get()
  getProfile() {
    return { message: 'This is your profile' };
  }
}

5. 完整模块示例

最后,将所有模块整合在 app.module.ts 中:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { ProfileController } from './auth/profile.controller';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'your_username',
      password: 'your_password',
      database: 'your_database',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true,
    }),
    UsersModule,
    AuthModule,
  ],
  controllers: [ProfileController],
})
export class AppModule {}

6. 总结

通过上述步骤,我们实现了在 Nest.js 中使用 JWT 进行用户认证与授权的完整流程。我们创建了用户实体、服务和控制器,并通过 JWT 策略和 Guards 保护了特定路由。这种方式不仅提高了安全性,还通过模块化的设计提高了代码的可维护性和可扩展性。

  • 用户注册与登录:使用 bcrypt 加密密码并生成 JWT 令牌。
  • 保护路由:通过 Guards 和策略确保只有认证用户可以访问特定资源。

Nest.js 的认证与授权机制灵活而强大,可以根据具体需求进行扩展和定制,适应各种复杂的应用场景。

评论区
评论列表
menu