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

如何在dotnet core IdentityModel中使用scopes保护API端点

概述

我有一个使用OAuth2Introspection保护的dotNet Web API,授权逻辑由该Web API验证第三方签发的reference token完成。该Web API包含多个端点,部分端点需要不同的scope(例如read和readwrite)。

问题

我要如何通过scopes控制每个端点的访问权限?我希望实现类似如下代码的效果:

using System;
using System.Collections.Generic;
using System.Data.Odbc;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace API.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class WeatherController : ControllerBase
    {
        private readonly ILogger<WeatherController> _logger;
        private readonly IConfiguration _config;

        public WeatherController(ILogger<WeatherController> logger, IConfiguration config)
        {
            _logger = logger;
            _config = config;
        }

        [HttpGet]
        [Authorize]
        [ScopeAuthorize("weather_read")]
        public ActionResult GetWeather()
        {
            return Ok();
        }

        [HttpPost]
        [Authorize]
        [ScopeAuthorize("weather_readwrite")]
        public ActionResult SetWeather(string weather)
        {
            return Ok();
        }
    }
}

实现方案

你可以通过自定义授权策略+动态策略提供器+自定义特性的方式实现需求,全程不需要预先定义每个scope的固定策略,完全符合你想要的代码写法,步骤如下:

  • 第一步:定义Scope授权要求类,继承IAuthorizationRequirement
using Microsoft.AspNetCore.Authorization;

public class ScopeRequirement : IAuthorizationRequirement
{
    public string RequiredScope { get; }
    public ScopeRequirement(string requiredScope)
    {
        RequiredScope = requiredScope ?? throw new ArgumentNullException(nameof(requiredScope));
    }
}
  • 第二步:实现Scope授权处理程序,从Token解析出的声明中校验所需scope
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;

public class ScopeHandler : AuthorizationHandler<ScopeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ScopeRequirement requirement)
    {
        // 兼容不同授权服务器返回的scope声明类型
        var scopes = context.User.Claims
            .Where(c => c.Type == "scope" || c.Type == ClaimTypes.Scope)
            .SelectMany(c => c.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries))
            .ToList();

        if (scopes.Contains(requirement.RequiredScope))
        {
            context.Succeed(requirement);
        }
        return Task.CompletedTask;
    }
}
  • 第三步:实现动态授权策略提供器,无需预先定义所有scope对应的策略
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;

public class ScopeAuthorizationPolicyProvider : IAuthorizationPolicyProvider
{
    private const string POLICY_PREFIX = "Scope:";
    private readonly DefaultAuthorizationPolicyProvider _defaultProvider;

    public ScopeAuthorizationPolicyProvider(IOptions<AuthorizationOptions> options)
    {
        _defaultProvider = new DefaultAuthorizationPolicyProvider(options);
    }

    public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => _defaultProvider.GetDefaultPolicyAsync();
    public Task<AuthorizationPolicy?> GetFallbackPolicyAsync() => _defaultProvider.GetFallbackPolicyAsync();

    public Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
    {
        if (policyName.StartsWith(POLICY_PREFIX, StringComparison.OrdinalIgnoreCase))
        {
            var scope = policyName.Substring(POLICY_PREFIX.Length);
            var policy = new AuthorizationPolicyBuilder()
                .AddRequirements(new ScopeRequirement(scope))
                .Build();
            return Task.FromResult<AuthorizationPolicy?>(policy);
        }
        return _defaultProvider.GetPolicyAsync(policyName);
    }
}
  • 第四步:实现你需要的ScopeAuthorize自定义特性
using Microsoft.AspNetCore.Authorization;

public class ScopeAuthorizeAttribute : AuthorizeAttribute
{
    public ScopeAuthorizeAttribute(string scope)
    {
        Policy = $"Scope:{scope}";
    }
}
  • 第五步:在Program.cs(.NET 6+)中注册相关服务,原有OAuth2内省配置保留即可
// 注册动态策略提供器
builder.Services.AddSingleton<IAuthorizationPolicyProvider, ScopeAuthorizationPolicyProvider>();
// 注册Scope授权处理程序
builder.Services.AddSingleton<IAuthorizationHandler, ScopeHandler>();

// 你原有配置保持不变即可
builder.Services.AddAuthentication(OAuth2IntrospectionDefaults.AuthenticationScheme)
    .AddOAuth2Introspection(options =>
    {
        options.Authority = "你的授权服务地址";
        options.ClientId = "你的API客户端ID";
        options.ClientSecret = "你的API客户端密钥";
        // 其余原有配置保留
    });

builder.Services.AddAuthorization();

配置完成后你原有Controller里的写法完全可以正常运行,调用接口时如果Token不包含对应scope会直接返回403状态码。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 10:57:07