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

ASP.NET项目级用户权限管理咨询:策略及存储方案探讨

Answer

Great question—this is such a common scenario for content-sharing apps, and ASP.NET has all the built-in tools you need to handle this properly, no need to reinvent the wheel entirely. Let’s break this down:

1. Yes, ASP.NET’s Policy Authorization is Perfect for This

Role-based authorization works great for global permissions (like "Admin" or "Basic User"), but it falls short when you need resource-specific (in your case, Project-specific) access control. That’s exactly what policy-based authorization was designed for. Here’s how to implement it:

Step 1: Define a Permission Requirement

First, create a custom requirement that represents the permission you want to check (e.g., viewing or editing a project):

public enum ProjectPermissionType
{
    View,
    Edit
}

public class ProjectPermissionRequirement : IAuthorizationRequirement
{
    public ProjectPermissionType RequiredPermission { get; }

    public ProjectPermissionRequirement(ProjectPermissionType requiredPermission)
    {
        RequiredPermission = requiredPermission;
    }
}

Step 2: Build an Authorization Handler

Next, write a handler that checks if the current user has the required permission for the target project. This is where you’ll query your database to look up the user’s permissions for that specific project:

public class ProjectPermissionHandler : AuthorizationHandler<ProjectPermissionRequirement, Project>
{
    private readonly YourDbContext _dbContext;

    public ProjectPermissionHandler(YourDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, 
        ProjectPermissionRequirement requirement, Project project)
    {
        // Get the current user's ID from their claims
        var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
        if (string.IsNullOrEmpty(userId))
        {
            context.Fail();
            return;
        }

        // Project creators get full permissions by default
        if (project.CreatorId == userId)
        {
            context.Succeed(requirement);
            return;
        }

        // Look up the user's explicit permission for this project
        var userPermission = await _dbContext.ProjectPermissions
            .FirstOrDefaultAsync(p => p.ProjectId == project.Id && p.UserId == userId);

        if (userPermission != null && userPermission.PermissionType == requirement.RequiredPermission)
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail();
        }
    }
}

Step 3: Register the Policy and Handler

In your Program.cs, register the handler and define named policies for each permission type:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("CanViewProject", policy =>
        policy.Requirements.Add(new ProjectPermissionRequirement(ProjectPermissionType.View)));
    
    options.AddPolicy("CanEditProject", policy =>
        policy.Requirements.Add(new ProjectPermissionRequirement(ProjectPermissionType.Edit)));
});

builder.Services.AddScoped<IAuthorizationHandler, ProjectPermissionHandler>();

Step 4: Use the Policy in Your Controllers

You can apply the policy to controller actions, and even explicitly check authorization for granular control:

[Authorize(Policy = "CanEditProject")]
public async Task<IActionResult> Edit(int projectId)
{
    var project = await _dbContext.Projects.FindAsync(projectId);
    if (project == null) return NotFound();

    // Optional: Explicit authorization check if you need custom failure logic
    var authResult = await _authorizationService.AuthorizeAsync(User, project, "CanEditProject");
    if (!authResult.Succeeded)
    {
        return Forbid();
    }

    // Proceed with your edit logic
    return View(project);
}

2. Storing User-Project Permissions is the Standard (and Safe) Approach

Your idea of storing user IDs alongside their project-specific permissions is exactly the right approach. Here’s a recommended database schema for this:

  • Projects Table: Id (primary key), CreatorId (foreign key to Users), Name, Description, and other project fields.
  • ProjectPermissions Table: Id (primary key), ProjectId (foreign key to Projects), UserId (foreign key to Users), PermissionType (enum or string representing View/Edit).

Key Security Notes for This Approach:

  • Never trust client input: Always validate that the user has permission to access the specific projectId they’re requesting—frontend checks are easy to bypass, so all logic must happen server-side.
  • Optimize database queries: When fetching projects for a user, filter directly in the database (e.g., _dbContext.Projects.Where(p => p.CreatorId == userId || p.Permissions.Any(perm => perm.UserId == userId && perm.PermissionType == View))) instead of fetching all projects and filtering in memory.
  • Cache strategically: If your app has high traffic, cache user permissions for a project to reduce database hits—just make sure to invalidate the cache immediately when permissions are updated.

3. Are There More Secure Alternatives?

This user-project permission model is the industry standard for resource-specific access control, and when implemented correctly with ASP.NET’s built-in tools, it’s highly secure. The main "alternatives" are just variations on this theme:

  • Cached claims: You could add project-specific claims to the user’s identity on login, but this only works if the number of projects per user is small—otherwise, the authentication token (JWT) will get too large and unwieldy.
  • ABAC (Attribute-Based Access Control): This is a more flexible model where permissions are based on attributes (e.g., "user is part of the project’s team"), but it’s overkill for most simple project-sharing apps. Your original approach is simpler and more maintainable for your use case.

Final Takeaway

Stick with ASP.NET’s policy authorization combined with a ProjectPermissions table—this gives you the best of both worlds: leveraging ASP.NET’s secure, battle-tested authorization framework while supporting the project-specific permission granularity you need. It’s scalable, maintainable, and aligns with best practices for this kind of app.

内容的提问来源于stack exchange,提问作者user57029

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:54:20