基于 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 策略验证的用户才能访问该路由。