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

ASP.NET Core同端点如何按认证架构选不同授权策略及相关问题

ASP.NET Core 6.0 双JWT认证的按需授权方案

一、配置双JWT认证方案

首先在Program.cs中注册两个独立的JWT认证方案,分别命名为jwt1和jwt2,配置各自的授权服务器地址、受众等参数:

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer("jwt1", options =>
    {
        options.Authority = "https://your-jwt1-issuer.com";
        options.Audience = "your-jwt1-audience";
        // 其他JWT验证配置(比如签名算法、时钟偏差等)
    })
    .AddJwtBearer("jwt2", options =>
    {
        options.Authority = "https://your-jwt2-issuer.com";
        options.Audience = "your-jwt2-audience";
        // 其他JWT验证配置
    });

二、实现按认证方案匹配授权策略

默认的[Authorize]属性会同时校验所有指定策略,无法实现“用哪个方案认证就用对应策略”的逻辑,我们需要自定义授权逻辑:

1. 自定义授权属性

创建一个继承自AuthorizeAttribute的自定义属性,用于绑定认证方案和对应的策略映射,并指定允许的认证方案:

public class AuthorizeBySchemeAttribute : AuthorizeAttribute
{
    public Dictionary<string, string> SchemePolicyMap { get; set; } = new();

    public AuthorizeBySchemeAttribute()
    {
        // 让认证中间件尝试jwt1或jwt2任一方案
        AuthenticationSchemes = "jwt1,jwt2";
    }
}

2. 实现授权处理程序

编写IAuthorizationHandler实现类,根据当前用户的认证方案匹配对应的策略并校验:

public class SchemeBasedAuthorizationHandler : IAuthorizationHandler
{
    private readonly IAuthorizationPolicyProvider _policyProvider;
    private readonly IAuthorizationService _authorizationService;

    public SchemeBasedAuthorizationHandler(IAuthorizationPolicyProvider policyProvider, IAuthorizationService authorizationService)
    {
        _policyProvider = policyProvider;
        _authorizationService = authorizationService;
    }

    public async Task HandleAsync(AuthorizationHandlerContext context)
    {
        // 获取当前用户使用的认证方案
        var authScheme = context.User.Identities.FirstOrDefault()?.AuthenticationType;
        if (string.IsNullOrEmpty(authScheme))
        {
            context.Fail();
            return;
        }

        // 获取端点上的自定义授权属性
        var authorizeAttr = context.Resource as EndpointMetadataCollection?
            ?.GetMetadata<AuthorizeBySchemeAttribute>();
        if (authorizeAttr == null || !authorizeAttr.SchemePolicyMap.TryGetValue(authScheme, out var policyName))
        {
            context.Fail();
            return;
        }

        // 获取并校验对应策略
        var policy = await _policyProvider.GetPolicyAsync(policyName);
        if (policy == null)
        {
            context.Fail();
            return;
        }

        var authResult = await _authorizationService.AuthorizeAsync(context.User, context.Resource, policy);
        if (authResult.Succeeded)
        {
            context.Succeed(context.Requirements.First());
        }
        else
        {
            context.Fail();
        }
    }
}

3. 注册策略与处理程序

在Program.cs中注册自定义授权策略和处理程序:

builder.Services.AddAuthorization(options =>
{
    // 配置jwt1对应的授权策略
    options.AddPolicy("jwt1_policy", policy =>
    {
        policy.RequireClaim("role", "admin"); // 示例:要求role声明为admin
    });

    // 配置jwt2对应的授权策略
    options.AddPolicy("jwt2_policy", policy =>
    {
        policy.RequireClaim("permission", "api_access"); // 示例:要求permission声明为api_access
    });
});

// 注册自定义授权处理程序
builder.Services.AddScoped<IAuthorizationHandler, SchemeBasedAuthorizationHandler>();

4. 端点使用示例

在API端点上应用自定义属性,绑定方案与策略:

[ApiController]
[Route("api/test")]
public class TestController : ControllerBase
{
    [HttpGet]
    [AuthorizeByScheme(SchemePolicyMap = new Dictionary<string, string>
    {
        {"jwt1", "jwt1_policy"},
        {"jwt2", "jwt2_policy"}
    })]
    public IActionResult GetData()
    {
        return Ok("授权通过");
    }
}

三、实现JWT1专属的Scope校验

要实现“仅通过jwt1认证时才校验Scope”的逻辑,我们可以自定义授权要求和处理程序:

1. 自定义Scope授权要求

创建一个包含目标Scope的授权要求:

public class Jwt1RequiredScopeRequirement : IAuthorizationRequirement
{
    public string RequiredScope { get; }

    public Jwt1RequiredScopeRequirement(string requiredScope)
    {
        RequiredScope = requiredScope;
    }
}

2. 实现Scope授权处理程序

编写处理程序,仅当用户通过jwt1认证时才校验Scope:

public class Jwt1ScopeAuthorizationHandler : AuthorizationHandler<Jwt1RequiredScopeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, Jwt1RequiredScopeRequirement requirement)
    {
        // 非jwt1认证直接跳过校验
        var authScheme = context.User.Identities.FirstOrDefault()?.AuthenticationType;
        if (authScheme != "jwt1")
        {
            context.Succeed(requirement);
            return Task.CompletedTask;
        }

        // 读取Token中的scope声明并校验
        var scopeClaimValue = context.User.FindFirst("scope")?.Value;
        if (string.IsNullOrEmpty(scopeClaimValue))
        {
            context.Fail();
            return Task.CompletedTask;
        }

        var scopes = scopeClaimValue.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        if (scopes.Contains(requirement.RequiredScope))
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail();
        }

        return Task.CompletedTask;
    }
}

3. 注册与使用

在Program.cs中注册处理程序,并可以创建自定义属性简化使用:

// 注册Scope授权处理程序
builder.Services.AddScoped<IAuthorizationHandler, Jwt1ScopeAuthorizationHandler>();

// 注册包含Scope要求的策略(可选)
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("jwt1_read_scope", policy =>
    {
        policy.Requirements.Add(new Jwt1RequiredScopeRequirement("read"));
    });
});

// 自定义Scope属性(可选)
public class Jwt1RequiredScopeAttribute : AuthorizeAttribute
{
    public Jwt1RequiredScopeAttribute(string scope)
    {
        Policy = $"jwt1_{scope}_scope";
    }
}

4. 端点使用示例

在需要Scope校验的端点上同时应用两个属性:

[HttpGet("scoped-data")]
[AuthorizeByScheme(SchemePolicyMap = new Dictionary<string, string>
{
    {"jwt1", "jwt1_policy"},
    {"jwt2", "jwt2_policy"}
})]
[Jwt1RequiredScope("read")]
// 或者直接用[Authorize(Policy = "jwt1_read_scope")]
public IActionResult GetScopedData()
{
    return Ok("JWT1认证且Scope校验通过,或JWT2认证通过");
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 15:43:19