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

基于ASP.NET MVC Core 2.0的采购系统文档细粒度权限控制技术咨询

Hey there! Let's break down how to build a robust, granular permission control system for your document operations in your ASP.NET MVC Core 2.0 app—since you already have an IPermissionControl interface, we can extend that into a solution that handles all your use cases (read, edit, delete, modify document numbers, and control access to specific properties).

Optimal Approach for Document Permission Control

1. Refine Permission Granularity to Match Your Needs

First, expand your permission model to cover specific actions + resource attributes instead of broad "edit" permissions. This lets you control access to individual document properties (like document number vs. supplier info) separately.

  • Create an enum to define all possible document actions:
    public enum DocumentPermissionAction
    {
        ReadDocument,
        DeleteDocument,
        EditDocumentBasicInfo,
        EditDocumentNumber,
        EditSpecificProperty // Use this for property-level control, e.g., "PurchaseDate" or "SupplierName"
    }
    
  • Update your IPermissionControl interface to support granular checks:
    public interface IPermissionControl
    {
        // Check permission for a user, document, action, and optional property
        bool HasPermission(User currentUser, Document targetDocument, DocumentPermissionAction action, string propertyName = null);
    }
    

2. Integrate with ASP.NET Core's Authorization Pipeline

Leverage custom authorization attributes to enforce permissions at the controller/action level—this keeps your permission logic centralized and avoids repeating checks in every action.

Here's an example of a custom attribute:

public class DocumentPermissionAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    private readonly DocumentPermissionAction _requiredAction;
    private readonly string _targetProperty;

    public DocumentPermissionAttribute(DocumentPermissionAction action, string propertyName = null)
    {
        _requiredAction = action;
        _targetProperty = propertyName;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        // Resolve dependencies from the service container
        var userManager = context.HttpContext.RequestServices.GetRequiredService<UserManager<User>>();
        var permissionControl = context.HttpContext.RequestServices.GetRequiredService<IPermissionControl>();
        var dbContext = context.HttpContext.RequestServices.GetRequiredService<AppDbContext>();

        // Get current authenticated user
        var currentUser = userManager.GetUserAsync(context.HttpContext.User).Result;
        if (currentUser == null)
        {
            context.Result = new UnauthorizedResult();
            return;
        }

        // Extract document ID from route (assumes your route uses "id" as the parameter)
        if (!int.TryParse(context.RouteData.Values["id"]?.ToString(), out int docId))
        {
            context.Result = new BadRequestResult();
            return;
        }

        // Fetch the target document from the database
        var targetDoc = dbContext.Documents.Find(docId);
        if (targetDoc == null)
        {
            context.Result = new NotFoundResult();
            return;
        }

        // Validate permission
        if (!permissionControl.HasPermission(currentUser, targetDoc, _requiredAction, _targetProperty))
        {
            context.Result = new ForbidResult(); // Return 403 Forbidden (logged in but no access)
            return;
        }
    }
}

Use this attribute in your controller to protect actions:

public class DocumentsController : Controller
{
    [DocumentPermission(DocumentPermissionAction.ReadDocument)]
    public IActionResult Details(int id)
    {
        // Fetch and return document details
    }

    [DocumentPermission(DocumentPermissionAction.EditDocumentNumber)]
    [HttpPost]
    public IActionResult UpdateDocumentNumber(int id, string newNumber)
    {
        // Update document number logic
    }

    [DocumentPermission(DocumentPermissionAction.EditSpecificProperty, "SupplierName")]
    [HttpPost]
    public IActionResult UpdateSupplier(int id, string newSupplier)
    {
        // Update supplier info logic
    }
}

3. Implement the IPermissionControl with EF Core

Build out your permission control implementation to query permissions from your MSSQL database via EF Core. You'll need a few database tables to model permissions:

  • Permissions: Stores all available permissions (e.g., DocumentRead, DocumentEditNumber)
  • UserPermissions: Maps users to their assigned permissions
  • DocumentPermissions (optional): For document-specific access (e.g., a user can only edit their own documents)

Here's a sample implementation:

public class PermissionControl : IPermissionControl
{
    private readonly AppDbContext _dbContext;
    private readonly IMemoryCache _cache; // Cache permissions to reduce DB hits

    public PermissionControl(AppDbContext dbContext, IMemoryCache cache)
    {
        _dbContext = dbContext;
        _cache = cache;
    }

    public bool HasPermission(User currentUser, Document targetDoc, DocumentPermissionAction action, string propertyName = null)
    {
        // Check if user is an admin (bypass all checks)
        if (IsAdmin(currentUser))
            return true;

        // Get cached user permissions (refresh every hour)
        var userPermissions = _cache.GetOrCreate($"UserPermissions_{currentUser.Id}", entry =>
        {
            entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1);
            return _dbContext.UserPermissions
                .Where(up => up.UserId == currentUser.Id)
                .Select(up => up.Permission.Name)
                .ToList();
        });

        // Check permissions based on action
        switch (action)
        {
            case DocumentPermissionAction.ReadDocument:
                return userPermissions.Contains("DocumentRead") || targetDoc.OwnerId == currentUser.Id;
            case DocumentPermissionAction.EditDocumentNumber:
                return userPermissions.Contains("DocumentEditNumber") || targetDoc.OwnerId == currentUser.Id;
            case DocumentPermissionAction.EditSpecificProperty:
                if (string.IsNullOrEmpty(propertyName)) return false;
                // Map property name to permission (e.g., "SupplierName" → "DocumentEditSupplier")
                var requiredPermission = $"DocumentEdit{propertyName}";
                return userPermissions.Contains(requiredPermission) || targetDoc.OwnerId == currentUser.Id;
            case DocumentPermissionAction.DeleteDocument:
                return userPermissions.Contains("DocumentDelete") || targetDoc.OwnerId == currentUser.Id;
            default:
                return false;
        }
    }

    private bool IsAdmin(User user)
    {
        // Check if user has admin role or permission
        return _dbContext.UserRoles.Any(ur => ur.UserId == user.Id && ur.Role.Name == "Admin");
    }
}

4. Enforce Permissions in the View Layer

Don't forget to hide or disable UI elements for actions the user can't perform. Inject IPermissionControl into your Razor views to conditionally render content:

@model Document
@inject IPermissionControl PermissionControl

<h1>@model.Title</h1>

<!-- Only show edit number button if user has permission -->
@if (PermissionControl.HasPermission(User, Model, DocumentPermissionAction.EditDocumentNumber))
{
    <div class="form-group">
        <label>Document Number</label>
        <input type="text" value="@model.DocumentNumber" id="docNumber" />
        <button onclick="updateDocNumber()">Save Changes</button>
    </div>
}

<!-- Only show supplier edit field if user has permission -->
@if (PermissionControl.HasPermission(User, Model, DocumentPermissionAction.EditSpecificProperty, "SupplierName"))
{
    <div class="form-group">
        <label>Supplier</label>
        <input type="text" value="@model.SupplierName" id="supplier" />
        <button onclick="updateSupplier()">Save Changes</button>
    </div>
}

5. Add Guard Clauses in Business Logic

Never rely solely on controller attributes—add permission checks in your service layer to prevent unauthorized access via direct service calls (e.g., from APIs or background jobs):

public class DocumentService
{
    private readonly IPermissionControl _permissionControl;
    private readonly AppDbContext _dbContext;

    public DocumentService(IPermissionControl permissionControl, AppDbContext dbContext)
    {
        _permissionControl = permissionControl;
        _dbContext = dbContext;
    }

    public void UpdateDocumentNumber(User currentUser, int docId, string newNumber)
    {
        var document = _dbContext.Documents.Find(docId);
        if (!_permissionControl.HasPermission(currentUser, document, DocumentPermissionAction.EditDocumentNumber))
        {
            throw new UnauthorizedAccessException("You do not have permission to edit this document's number.");
        }

        // Proceed with update logic
        document.DocumentNumber = newNumber;
        _dbContext.SaveChanges();
    }
}
Key Notes for Your Tech Stack
  • EF Core 2.0: Use Include() to avoid N+1 queries when fetching user permissions, and make sure your DbContext is properly configured for dependency injection.
  • MSSQL: Index your permission tables (e.g., UserPermissions.UserId, DocumentPermissions.DocumentId) to speed up permission checks.
  • Caching: The memory cache in the example reduces database load—adjust the expiration time based on how often permissions change in your system.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 04:17:18