遵循整洁架构的NestJS应用:用户高级功能访问权限验证
基于整洁架构的NestJS高级功能权限验证方案
针对你的需求,核心要守住整洁架构的依赖规则:内层(实体/业务规则)不依赖外层,外层(控制器/守卫)只能依赖内层。下面是具体实现思路和代码示例:
核心原则落地
- 业务规则归位实体层:把"什么用户能访问什么高级功能"的核心逻辑放在最内层的用户实体中,确保规则不依赖任何框架或外层代码。
- 用例层编排逻辑:用例只负责调用实体的权限方法,不硬编码规则,保持业务流程的清晰。
- 外层只做转发:守卫、装饰器等应用层组件仅负责传递请求数据和元数据,实际验证逻辑委托给核心层的服务/实体。
具体实现步骤
1. 实体层:定义核心权限规则
把权限判断逻辑封装在用户实体中,这是整洁架构最内层的业务规则,可独立测试、复用。
// domain/entities/user.entity.ts export class User { id: string; subscriptionTier: 'free' | 'premium'; subscriptionExpiresAt: Date; // 核心业务规则:判断用户是否有权访问指定高级功能 hasPremiumAccess(feature: string): boolean { // 基础校验:订阅类型+有效期 if (this.subscriptionTier !== 'premium' || this.subscriptionExpiresAt < new Date()) { return false; } // 高级功能映射(可抽成领域常量,这里简化) const allowedPremiumFeatures = [ 'can-create-more-than-10-posts', 'unlimited-storage', 'priority-support' ]; return allowedPremiumFeatures.includes(feature); } }
2. 领域服务:封装复杂验证逻辑(可选)
如果权限规则涉及多实体交互(比如用户角色+订阅组合),可以抽成领域服务,仍依赖实体层,不碰外层代码。
// domain/services/permission.service.ts import { User } from '../entities/user.entity'; export class PermissionService { checkPremiumAccess(user: User, feature: string): boolean { return user.hasPremiumAccess(feature); // 可扩展复杂规则:比如结合用户角色、团队权限等 } }
3. 用例层:编排业务流程
在需要权限校验的用例中,调用领域服务/实体的方法,确保业务流程的完整性(即使守卫漏过,用例也能兜底验证)。
// use-cases/create-post.use-case.ts import { User } from '../domain/entities/user.entity'; import { PermissionService } from '../domain/services/permission.service'; import { InsufficientPermissionsError } from '../domain/errors/insufficient-permissions.error'; export class CreatePostUseCase { constructor( private readonly userRepository: UserRepository, // 接口适配器层的仓库,依赖实体 private readonly permissionService: PermissionService ) {} async execute(input: { userId: string; content: string; postCount: number }) { const user = await this.userRepository.findById(input.userId); // 当创建数量超过10时,校验高级权限 if (input.postCount >= 10) { const hasAccess = this.permissionService.checkPremiumAccess(user, 'can-create-more-than-10-posts'); if (!hasAccess) { throw new InsufficientPermissionsError('需开通高级订阅才能创建超过10篇帖子'); } } // 执行创建帖子的核心业务逻辑 // ... } }
4. 接口适配器层:守卫+装饰器实现前置校验
用守卫做请求拦截,但守卫仅负责传递数据,不包含业务规则,避免违反整洁架构。
自定义装饰器:标记需要权限的接口
// src/auth/decorators/premium-feature.decorator.ts import { SetMetadata } from '@nestjs/common'; // 仅存储元数据,不包含任何业务逻辑 export const PremiumFeature = (feature: string) => SetMetadata('required-premium-feature', feature);
守卫:调用核心层服务做验证
// src/auth/guards/premium-feature.guard.ts import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { PermissionService } from '../../domain/services/permission.service'; import { User } from '../../domain/entities/user.entity'; @Injectable() export class PremiumFeatureGuard implements CanActivate { constructor( private readonly reflector: Reflector, private readonly permissionService: PermissionService // 依赖核心层服务 ) {} canActivate(context: ExecutionContext): boolean { // 从装饰器获取需要验证的功能 const requiredFeature = this.reflector.get<string>( 'required-premium-feature', context.getHandler() ); if (!requiredFeature) return true; // 无标记则直接通过 // 从请求中获取已认证的用户(需确保是领域实体,而非数据库DTO) const request = context.switchToHttp().getRequest(); const user: User = request.user; // 委托核心层服务做验证,守卫仅做转发 return this.permissionService.checkPremiumAccess(user, requiredFeature); } }
控制器:复用守卫和装饰器
// src/posts/posts.controller.ts import { Controller, Post, UseGuards, Request, Body } from '@nestjs/common'; import { PremiumFeature } from '../auth/decorators/premium-feature.decorator'; import { PremiumFeatureGuard } from '../auth/guards/premium-feature.guard'; import { CreatePostUseCase } from '../domain/use-cases/create-post.use-case'; @Controller('posts') export class PostsController { constructor(private readonly createPostUseCase: CreatePostUseCase) {} @Post() @UseGuards(PremiumFeatureGuard) @PremiumFeature('can-create-more-than-10-posts') async createPost( @Request() req, @Body() body: { content: string; postCount: number } ) { const input = { userId: req.user.id, content: body.content, postCount: body.postCount }; await this.createPostUseCase.execute(input); return { message: '帖子创建成功' }; } }
方案优势
- 符合整洁架构:核心业务规则完全在内层,外层仅做调用,无反向依赖。
- 避免冗余:通过守卫+装饰器可在多个控制器方法上复用权限校验,用例层的兜底验证确保安全性。
- 可维护性:权限规则的修改仅需调整实体/领域服务,无需改动外层的控制器或守卫。
内容的提问来源于stack exchange,提问作者SWAPNIL KUWAR
相关产品推荐
相关产品推荐

