马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
在上一篇文章中,我们先容了 NestJS 的数据库操纵和 TypeORM 集成。本文将深入探究如安在 NestJS 中实现完备的认证和授权体系。
JWT 认证实现
1. 安装依赖
- npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt
- npm install -D @types/passport-jwt @types/bcrypt
复制代码 2. JWT 战略配置
- // src/auth/strategies/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(private configService: ConfigService) {
- super({
- jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
- ignoreExpiration: false,
- secretOrKey: configService.get('JWT_SECRET'),
- });
- }
- async validate(payload: any) {
- return {
- userId: payload.sub,
- username: payload.username,
- roles: payload.roles
- };
- }
- }
- // 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 { JwtStrategy } from './strategies/jwt.strategy';
- import { AuthService } from './auth.service';
- @Module({
- imports: [
- PassportModule.register({ defaultStrategy: 'jwt' }),
- JwtModule.registerAsync({
- imports: [ConfigModule],
- useFactory: async (configService: ConfigService) => ({
- secret: configService.get('JWT_SECRET'),
- signOptions: {
- expiresIn: '1d',
- issuer: 'nestjs-app'
- },
- }),
- inject: [ConfigService],
- }),
- ],
- providers: [AuthService, JwtStrategy],
- exports: [AuthService],
- })
- export class AuthModule {}
复制代码 3. 认证服务实现
- // src/auth/auth.service.ts
- import { Injectable, UnauthorizedException } from '@nestjs/common';
- import { JwtService } from '@nestjs/jwt';
- import { UsersService } from '../users/users.service';
- import * as bcrypt from 'bcrypt';
- @Injectable()
- export class AuthService {
- constructor(
- private usersService: UsersService,
- private jwtService: JwtService,
- ) {}
- async validateUser(username: string, password: string): Promise<any> {
- const user = await this.usersService.findByUsername(username);
- if (user && await bcrypt.compare(password, user.password)) {
- const { password, ...result } = user;
- return result;
- }
- return null;
- }
- async login(user: any) {
- const payload = {
- username: user.username,
- sub: user.id,
- roles: user.roles
- };
- return {
- access_token: this.jwtService.sign(payload),
- user: {
- id: user.id,
- username: user.username,
- email: user.email,
- roles: user.roles
- }
- };
- }
- async register(createUserDto: CreateUserDto) {
- // 检查用户是否已存在
- const existingUser = await this.usersService.findByUsername(
- createUserDto.username
- );
- if (existingUser) {
- throw new ConflictException('Username already exists');
- }
- // 加密密码
- const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
- // 创建新用户
- const newUser = await this.usersService.create({
- ...createUserDto,
- password: hashedPassword,
- });
- // 返回用户信息和令牌
- const { password, ...result } = newUser;
- return this.login(result);
- }
- }
复制代码 4. 认证控制器
- // src/auth/auth.controller.ts
- import { Controller, Post, Body, UseGuards, Get } from '@nestjs/common';
- import { AuthService } from './auth.service';
- import { JwtAuthGuard } from './guards/jwt-auth.guard';
- import { GetUser } from './decorators/get-user.decorator';
- @Controller('auth')
- export class AuthController {
- constructor(private authService: AuthService) {}
- @Post('login')
- async login(@Body() loginDto: LoginDto) {
- const user = await this.authService.validateUser(
- loginDto.username,
- loginDto.password
- );
- if (!user) {
- throw new UnauthorizedException('Invalid credentials');
- }
- return this.authService.login(user);
- }
- @Post('register')
- async register(@Body() createUserDto: CreateUserDto) {
- return this.authService.register(createUserDto);
- }
- @UseGuards(JwtAuthGuard)
- @Get('profile')
- getProfile(@GetUser() user: any) {
- return user;
- }
- }
复制代码 OAuth2.0 集成
1. Google OAuth2 实现
- // src/auth/strategies/google.strategy.ts
- import { PassportStrategy } from '@nestjs/passport';
- import { Strategy, VerifyCallback } from 'passport-google-oauth20';
- import { Injectable } from '@nestjs/common';
- import { ConfigService } from '@nestjs/config';
- @Injectable()
- export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
- constructor(private configService: ConfigService) {
- super({
- clientID: configService.get('GOOGLE_CLIENT_ID'),
- clientSecret: configService.get('GOOGLE_CLIENT_SECRET'),
- callbackURL: 'http://localhost:3000/auth/google/callback',
- scope: ['email', 'profile'],
- });
- }
- async validate(
- accessToken: string,
- refreshToken: string,
- profile: any,
- done: VerifyCallback,
- ): Promise<any> {
- const { name, emails, photos } = profile;
- const user = {
- email: emails[0].value,
- firstName: name.givenName,
- lastName: name.familyName,
- picture: photos[0].value,
- accessToken,
- };
- done(null, user);
- }
- }
- // src/auth/controllers/oauth.controller.ts
- import { Controller, Get, UseGuards, Req } from '@nestjs/common';
- import { AuthGuard } from '@nestjs/passport';
- @Controller('auth/google')
- export class OAuthController {
- @Get()
- @UseGuards(AuthGuard('google'))
- async googleAuth(@Req() req) {}
- @Get('callback')
- @UseGuards(AuthGuard('google'))
- async googleAuthRedirect(@Req() req) {
- // 处理 Google 认证回调
- return this.authService.googleLogin(req.user);
- }
- }
复制代码 2. GitHub OAuth2 实现
- // src/auth/strategies/github.strategy.ts
- import { Injectable } from '@nestjs/common';
- import { PassportStrategy } from '@nestjs/passport';
- import { Strategy } from 'passport-github2';
- import { ConfigService } from '@nestjs/config';
- @Injectable()
- export class GithubStrategy extends PassportStrategy(Strategy, 'github') {
- constructor(private configService: ConfigService) {
- super({
- clientID: configService.get('GITHUB_CLIENT_ID'),
- clientSecret: configService.get('GITHUB_CLIENT_SECRET'),
- callbackURL: 'http://localhost:3000/auth/github/callback',
- scope: ['user:email'],
- });
- }
- async validate(
- accessToken: string,
- refreshToken: string,
- profile: any,
- done: Function,
- ) {
- const user = {
- githubId: profile.id,
- username: profile.username,
- email: profile.emails[0].value,
- accessToken,
- };
- done(null, user);
- }
- }
复制代码 RBAC 权限控制
1. 角色定义
- // src/auth/enums/role.enum.ts
- export enum Role {
- USER = 'user',
- ADMIN = 'admin',
- MODERATOR = 'moderator',
- }
- // src/auth/decorators/roles.decorator.ts
- import { SetMetadata } from '@nestjs/common';
- import { Role } from '../enums/role.enum';
- export const ROLES_KEY = 'roles';
- export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);
复制代码 2. 角色守卫
- // src/auth/guards/roles.guard.ts
- import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
- import { Reflector } from '@nestjs/core';
- import { Role } from '../enums/role.enum';
- import { ROLES_KEY } from '../decorators/roles.decorator';
- @Injectable()
- export class RolesGuard implements CanActivate {
- constructor(private reflector: Reflector) {}
- canActivate(context: ExecutionContext): boolean {
- const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
- context.getHandler(),
- context.getClass(),
- ]);
- if (!requiredRoles) {
- return true;
- }
- const { user } = context.switchToHttp().getRequest();
- return requiredRoles.some((role) => user.roles?.includes(role));
- }
- }
复制代码 3. 权限实体设计
- // src/auth/entities/permission.entity.ts
- import { Entity, Column, ManyToMany } from 'typeorm';
- import { BaseEntity } from '../../common/entities/base.entity';
- import { Role } from '../enums/role.enum';
- @Entity('permissions')
- export class Permission extends BaseEntity {
- @Column()
- name: string;
- @Column()
- description: string;
- @Column('simple-array')
- allowedRoles: Role[];
- }
- // src/users/entities/user.entity.ts
- import { Entity, Column, ManyToMany, JoinTable } from 'typeorm';
- import { Role } from '../../auth/enums/role.enum';
- import { Permission } from '../../auth/entities/permission.entity';
- @Entity('users')
- export class User extends BaseEntity {
- // ... 其他字段
- @Column('simple-array')
- roles: Role[];
- @ManyToMany(() => Permission)
- @JoinTable({
- name: 'user_permissions',
- joinColumn: { name: 'user_id' },
- inverseJoinColumn: { name: 'permission_id' },
- })
- permissions: Permission[];
- }
复制代码 4. 权限查抄服务
- // src/auth/services/permission.service.ts
- import { Injectable, ForbiddenException } from '@nestjs/common';
- import { InjectRepository } from '@nestjs/typeorm';
- import { Repository } from 'typeorm';
- import { Permission } from '../entities/permission.entity';
- import { User } from '../../users/entities/user.entity';
- @Injectable()
- export class PermissionService {
- constructor(
- @InjectRepository(Permission)
- private permissionRepository: Repository<Permission>,
- ) {}
- async checkPermission(user: User, permissionName: string): Promise<boolean> {
- const permission = await this.permissionRepository.findOne({
- where: { name: permissionName },
- });
- if (!permission) {
- throw new ForbiddenException('Permission not found');
- }
- // 检查用户角色是否有权限
- return permission.allowedRoles.some(role => user.roles.includes(role));
- }
- async grantPermission(user: User, permissionName: string): Promise<void> {
- const permission = await this.permissionRepository.findOne({
- where: { name: permissionName },
- });
- if (!permission) {
- throw new ForbiddenException('Permission not found');
- }
- user.permissions = [...user.permissions, permission];
- await this.userRepository.save(user);
- }
- }
复制代码 5. 利用示例
- // src/posts/posts.controller.ts
- import { Controller, Post, Body, UseGuards } from '@nestjs/common';
- import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
- import { RolesGuard } from '../auth/guards/roles.guard';
- import { Roles } from '../auth/decorators/roles.decorator';
- import { Role } from '../auth/enums/role.enum';
- import { GetUser } from '../auth/decorators/get-user.decorator';
- import { User } from '../users/entities/user.entity';
- import { PostsService } from './posts.service';
- import { CreatePostDto } from './dto/create-post.dto';
- @Controller('posts')
- @UseGuards(JwtAuthGuard, RolesGuard)
- export class PostsController {
- constructor(
- private postsService: PostsService,
- private permissionService: PermissionService,
- ) {}
- @Post()
- @Roles(Role.USER)
- async createPost(
- @GetUser() user: User,
- @Body() createPostDto: CreatePostDto,
- ) {
- // 检查用户是否有创建文章的权限
- const hasPermission = await this.permissionService.checkPermission(
- user,
- 'create_post'
- );
- if (!hasPermission) {
- throw new ForbiddenException('You do not have permission to create posts');
- }
- return this.postsService.create(user, createPostDto);
- }
- @Post('publish')
- @Roles(Role.MODERATOR, Role.ADMIN)
- async publishPost(
- @GetUser() user: User,
- @Body('postId') postId: string,
- ) {
- return this.postsService.publish(user, postId);
- }
- }
复制代码 安全最佳实践
1. 密码加密
- // src/common/utils/crypto.util.ts
- import * as bcrypt from 'bcrypt';
- import * as crypto from 'crypto';
- export class CryptoUtil {
- static async hashPassword(password: string): Promise<string> {
- const salt = await bcrypt.genSalt(10);
- return bcrypt.hash(password, salt);
- }
- static async comparePasswords(
- password: string,
- hashedPassword: string,
- ): Promise<boolean> {
- return bcrypt.compare(password, hashedPassword);
- }
- static generateRandomToken(length: number = 32): string {
- return crypto.randomBytes(length).toString('hex');
- }
- }
复制代码 2. 请求限流
- // src/common/guards/throttle.guard.ts
- import { Injectable } from '@nestjs/common';
- import { ThrottlerGuard } from '@nestjs/throttler';
- @Injectable()
- export class CustomThrottlerGuard extends ThrottlerGuard {
- protected getTracker(req: Record<string, any>): string {
- return req.ips.length ? req.ips[0] : req.ip;
- }
- }
- // src/app.module.ts
- import { Module } from '@nestjs/common';
- import { ThrottlerModule } from '@nestjs/throttler';
- @Module({
- imports: [
- ThrottlerModule.forRoot({
- ttl: 60,
- limit: 10,
- }),
- ],
- })
- export class AppModule {}
复制代码 3. 安全头部配置
- // src/main.ts
- import { NestFactory } from '@nestjs/core';
- import { AppModule } from './app.module';
- import * as helmet from 'helmet';
- async function bootstrap() {
- const app = await NestFactory.create(AppModule);
- // 安全头部
- app.use(helmet());
- // CORS 配置
- app.enableCors({
- origin: process.env.ALLOWED_ORIGINS.split(','),
- methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
- credentials: true,
- });
- await app.listen(3000);
- }
- bootstrap();
复制代码 写在最后
本文详细先容了 NestJS 中的认证与授权实现:
- JWT 认证的完备实现
- OAuth2.0 社交登录集成
- RBAC 权限控制体系
- 安全最佳实践
在下一篇文章中,我们将探究 NestJS 的中间件与拦截器实现。
假如以为这篇文章对你有资助,别忘了点个赞 |