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

ASP.NET Core 6 MVC如何动态创建授权策略?

最优实现方案

针对ASP.NET Core 6 MVC中动态生成控件级授权策略的需求,以下是落地性强的实现方案:

1. 自定义动态授权策略提供器

默认的授权策略提供器是静态的,我们需要实现IAuthorizationPolicyProvider来支持从数据库动态加载策略逻辑:

public class DynamicAuthorizationPolicyProvider : DefaultAuthorizationPolicyProvider
{
    private readonly IServiceScopeFactory _scopeFactory;

    public DynamicAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options, IServiceScopeFactory scopeFactory) 
        : base(options)
    {
        _scopeFactory = scopeFactory;
    }

    public override async Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
    {
        // 优先读取静态配置的策略
        var staticPolicy = await base.GetPolicyAsync(policyName);
        if (staticPolicy != null)
            return staticPolicy;

        // 从数据库加载当前策略对应的角色权限
        using var scope = _scopeFactory.CreateScope();
        var permissionRepo = scope.ServiceProvider.GetRequiredService<IPermissionRepository>();
        var allowedRoles = await permissionRepo.GetRolesForControlAsync(policyName);

        if (allowedRoles?.Any() != true)
            return null;

        // 构建动态授权策略:要求用户拥有至少一个匹配角色
        var policyBuilder = new AuthorizationPolicyBuilder();
        policyBuilder.RequireRole(allowedRoles.ToArray());
        return policyBuilder.Build();
    }
}

在Program.cs中注册这个自定义提供器:

builder.Services.AddSingleton<IAuthorizationPolicyProvider, DynamicAuthorizationPolicyProvider>();

2. 权限数据的缓存优化

直接每次请求查数据库会影响性能,建议做两层缓存:

  • 全局缓存:将所有控件-角色的映射关系缓存到内存/Redis,定时刷新(比如5分钟)或在权限变更时主动更新。
  • 用户级缓存:用户登录成功后,将其可访问的控件列表存入HttpContext.Session,减少授权时的数据库查询次数。

登录逻辑示例:

public async Task<IActionResult> Login(LoginViewModel model)
{
    // 用户身份验证逻辑...
    var user = await _userManager.FindByNameAsync(model.UserName);
    var userAccessibleControls = await _permissionRepo.GetUserAccessibleControls(user.Id);
    HttpContext.Session.SetString("UserControls", JsonSerializer.Serialize(userAccessibleControls));
    
    // 完成登录...
    return RedirectToAction("Index", "Home");
}

3. 视图层控件权限控制

自定义Tag Helper来快速控制视图中控件的显示/隐藏,无需重复编写判断逻辑:

[HtmlTargetElement("button", Attributes = "asp-permission")]
[HtmlTargetElement("input", Attributes = "asp-permission")]
[HtmlTargetElement("a", Attributes = "asp-permission")]
public class PermissionTagHelper : TagHelper
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IAuthorizationService _authorizationService;

    public PermissionTagHelper(IHttpContextAccessor httpContextAccessor, IAuthorizationService authorizationService)
    {
        _httpContextAccessor = httpContextAccessor;
        _authorizationService = authorizationService;
    }

    [HtmlAttributeName("asp-permission")]
    public string PolicyName { get; set; } = string.Empty;

    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        var httpContext = _httpContextAccessor.HttpContext;
        if (httpContext == null)
        {
            output.SuppressOutput();
            return;
        }

        var authResult = await _authorizationService.AuthorizeAsync(httpContext.User, PolicyName);
        if (!authResult.Succeeded)
        {
            output.SuppressOutput(); // 无权限则隐藏控件
        }
    }
}

在_ViewImports.cshtml中注册Tag Helper:

@addTagHelper *, YourProjectAssemblyName

视图中使用示例:

<button asp-permission="Order_Create">创建订单</button>
<input type="text" asp-permission="Order_Edit" placeholder="订单名称" />
<a asp-action="Delete" asp-permission="Order_Delete">删除订单</a>

4. 权限变更的实时生效

如果需要权限变更后用户无需重新登录即可生效:

  • 在DynamicAuthorizationPolicyProvider中使用带过期时间的缓存,或者在权限更新接口中主动清除对应缓存条目。
  • 可选结合SignalR,当权限变更时向前端推送通知,触发页面刷新或权限重新加载。

5. 双重验证保障

除了视图层控制,Action层也要添加授权验证,防止用户通过直接访问路由绕过视图限制:

[Authorize(Policy = "Order_Create")]
public IActionResult CreateOrder()
{
    return View();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 10:35:26