在现代 Web 应用中,认证与授权是确保安全性的关键部分。Nest.js 提供了强大的工具来实现这些功能,尤其是通过 JWT(JSON Web Token)认证机制、Guards 和策略。本文将详细介绍如何在 Nest.js 中实现 JWT 认证与授权,并结合具体示例进行讲解。
JWT 是一种开放标准(RFC 7519),用于在各方之间以 JSON 对象的方式安全地传输信息。JWT 由三部分组成:头部(Header)、有效载荷(Payload)和签名(Signature)。通过 JWT,用户可以在登录后获得一个令牌,该令牌在后续请求中作为身份凭证。
首先,我们需要安装一些必要的依赖,包括 @nestjs/jwt
和 passport
相关的包。
npm install @nestjs/passport passport passport-jwt @nestjs/jwt bcrypt
我们首先需要一个用户实体,用于数据库存储用户信息。在 users
文件夹中创建 user.entity.ts
:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
@Column()
password: string;
}
在 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 });
}
}
接下来,创建一个认证模块 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 {}
在 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),
};
}
}
在 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);
}
}
Guards 用于实现请求的授权逻辑,可以通过实现 CanActivate
接口来定义。策略用于验证用户身份。
在 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);
}
}
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);
}
}
在需要保护的控制器中使用 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' };
}
}
最后,将所有模块整合在 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 {}
通过上述步骤,我们实现了在 Nest.js 中使用 JWT 进行用户认证与授权的完整流程。我们创建了用户实体、服务和控制器,并通过 JWT 策略和 Guards 保护了特定路由。这种方式不仅提高了安全性,还通过模块化的设计提高了代码的可维护性和可扩展性。
Nest.js 的认证与授权机制灵活而强大,可以根据具体需求进行扩展和定制,适应各种复杂的应用场景。