基于 NestJS 使用 Passport 本地策略和 JWT 策略的详细步骤:

1. 安装依赖

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

npm install @nestjs/passport passport passport-local @nestjs/jwt bcryptjs

2. 创建本地策略

本地策略在 src/auth/local.strategy.ts 中定义。以下是该文件的内容:

import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { User } from 'src/user/entities/user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { UnauthorizedException } from '@nestjs/common';
import { compareSync } from 'bcryptjs';

export class LocalStrategy extends PassportStrategy(Strategy, 'local') {
  constructor(
    @InjectRepository(User)
    private readonly UserRepository: Repository<User>
  ) {
    super({
      usernameField: 'username',
      passwordField: 'password'
    });
  }

  async validate(username: string, password: string): Promise<any> {
    if (!username || !password) {
      throw new UnauthorizedException('用户名和密码不能为空');
    }

    const user = await this.UserRepository.findOne({
      where: { username },
      select: ['id', 'username', 'password', 'role']
    });

    if (!user) {
      throw new UnauthorizedException('用户不存在');
    }
    if (!compareSync(password, user.password)) {
      throw new UnauthorizedException('账户密码不正确');
    }
    // 删除密码字段,避免返回给客户端
    delete user.password;
    console.log('=====================strategy-user' + JSON.stringify(user));
    return user;
  }
}

3. 创建 JWT 策略

JWT 策略在 src/auth/jwt.strategy.ts 中定义。以下是该文件的内容:

import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from 'src/user/entities/user.entity';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    private configService: ConfigService,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: configService.get('JWT_SECRET', 'test123456'), // 确保在环境变量中设置了 JWT_SECRET
    });
  }

  async validate(payload: User): Promise<User> {
    const { username } = payload;
    const user = await this.userRepository.findOne({
      where: { username },
      select: ['id', 'username', 'role'], // 只选择需要的字段
    });

    if (!user) {
      throw new UnauthorizedException('Token 无效');
    }

    return user;
  }
}

4. 在Module中配置JWT,导入模块,注册本地和JWT策略

认证模块在 src/auth/auth.module.ts 中定义,使用JwtModule.registerAsync()配置jwt选项。导入 PassportModule, jwtModule,注册LocalStrategy, JwtStrategy,以下是该文件的内容:

import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { UserService } from 'src/user/user.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/user/entities/user.entity';
import { PassportModule } from '@nestjs/passport';
import { LocalStrategy } from './local.strategy';
import { AuthService } from './auth.service';
import { JwtModule } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { JwtStrategy } from './jwt.strategy';

const jwtModule = JwtModule.registerAsync({// JWT 模块配置方法
  inject: [ConfigService],
  useFactory: async (configService: ConfigService) => {
    return {
      secret: configService.get('JWT_SECRET', 'test123456'),//读取环境变量
      signOptions: { expiresIn: '4h' },
    };
  },
});

@Module({
  imports: [TypeOrmModule.forFeature([User]), PassportModule, jwtModule],
  controllers: [AuthController],
  providers: [LocalStrategy, AuthService, JwtStrategy],
})
export class AuthModule {}

5. 创建认证服务

认证服务在 src/auth/auth.service.ts 中定义。以下是该文件的内容:使用JwtService创建token,并在用户登陆后返回token。(在jwt策略中解析token得到payload)

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { User } from '../user/entities/user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class AuthService {
  constructor(
    private jwtService: JwtService,
    @InjectRepository(User)
    private userRepository: Repository<User>
  ) { }

  createToken(user: any) {
    return this.jwtService.sign(user);
  }

  async login(user: any) {
    console.log('=====================token-user' + JSON.stringify(user));
    const token = this.createToken({
      id: user.id,
      username: user.username,
      role: user.role
    })
    return { token };
  }

  async getUserInfo(user) {
    const info = await this.userRepository.findOne({
      where: { username: user.username }
    })
    return info
  }
}

6. 创建认证控制器

认证控制器在 src/auth/auth.controller.ts 中定义。以下是该文件的内容:

import { Body, Controller, Get, Post, Req, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiTags } from '@nestjs/swagger';
import { AuthService } from './auth.service';

@Controller('auth')
@ApiTags('用户注册登陆相关')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('register')
  async register(@Body() dto) {
    return this.authService.createToken(dto);
  }

  @Post('login')
  @UseGuards(AuthGuard('local'))//使用本地策略守卫
  async login(@Body() dto, @Req() req) {
    console.log(req.user);
    return this.authService.login(req.user);
  }

  @Get('userInfo')
  @UseGuards(AuthGuard('jwt'))//使用JWT策略守卫
  async getUserInfo(@Req() req) {
    return this.authService.getUserInfo(req.user);
  }
}

总结

auth.controller.ts 中,login 路由使用了 AuthGuard('local') 来保护,确保只有通过本地策略验证的用户才能访问该路由,getUserInfo 路由使用了 AuthGuard('jwt') 来保护,确保只有通过 JWT 策略验证的用户才能访问该路由。

最后修改:2024 年 12 月 03 日
如果觉得我的文章对你有用,请随意赞赏