You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

遵循整洁架构的NestJS应用:用户高级功能访问权限验证

基于整洁架构的NestJS高级功能权限验证方案

针对你的需求,核心要守住整洁架构的依赖规则:内层(实体/业务规则)不依赖外层,外层(控制器/守卫)只能依赖内层。下面是具体实现思路和代码示例:

核心原则落地

  1. 业务规则归位实体层:把"什么用户能访问什么高级功能"的核心逻辑放在最内层的用户实体中,确保规则不依赖任何框架或外层代码。
  2. 用例层编排逻辑:用例只负责调用实体的权限方法,不硬编码规则,保持业务流程的清晰。
  3. 外层只做转发:守卫、装饰器等应用层组件仅负责传递请求数据和元数据,实际验证逻辑委托给核心层的服务/实体。

具体实现步骤

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 00:50:18