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

如何将AuthorizationHandler中的数据传递至Controller

如何将AuthorizationHandler中的ApiKey对象传递到Controller

嘿,我明白你现在的需求——已经搞定了APIKey的校验逻辑,但想把查到的ApiKey对象传到Controller里直接用它的属性对吧?这事儿其实不难,我给你分享两个实用的方案:


方案一:存入HttpContext.Items(最直接简单)

HttpContext.Items是请求范围内的临时存储容器,刚好适合在Handler和Controller之间传递数据。你只需要在Handler校验通过后,把找到的ApiKey对象存进去,然后在Controller里取出来就行。

修改你的ApiKeyRequirementHandler

在context.Succeed(requirement);之后添加存储逻辑,顺便优化下查询效率:

public class ApiKeyRequirementHandler : AuthorizationHandler<ApiKeyRequirement> 
{ 
    private readonly IHttpContextAccessor _contextAccessor; 
    private readonly ApplicationDbContext _appContext; 
    private readonly ILogger<ApiKeyRequirementHandler> _logger; 

    public ApiKeyRequirementHandler(IHttpContextAccessor contextAccessor, ApplicationDbContext appContext, ILogger<ApiKeyRequirementHandler> logger) 
    { 
        _contextAccessor = contextAccessor; 
        _appContext = appContext; 
        _logger = logger; 
    } 

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ApiKeyRequirement requirement) 
    { 
        string rawAuthHeader = _contextAccessor.HttpContext.Request.Headers["Authorization"]; 
        if(string.IsNullOrEmpty(rawAuthHeader)) 
        { 
            return Task.CompletedTask; 
        } 

        string sentApiKey = "";
        // 简化Header解析逻辑,找到APIKEY就停止循环
        foreach(string header in rawAuthHeader.Split(","))
        {
            if(header.Trim().StartsWith("APIKEY"))
            {
                string[] splitHeader = header.Trim().Split(" ", StringSplitOptions.RemoveEmptyEntries);
                if(splitHeader.Length == 2)
                {
                    sentApiKey = splitHeader[1];
                    break;
                }
            }
        }

        // 直接查询第一个匹配的有效APIKey,无需循环遍历
        var foundKey = _appContext.apikey
            .FirstOrDefault(a => a.apikey == sentApiKey && a.isactive);

        if(foundKey != null)
        {
            // 将验证通过的ApiKey存入HttpContext.Items
            _contextAccessor.HttpContext.Items["ValidApiKey"] = foundKey;
            context.Succeed(requirement);
        } 

        return Task.CompletedTask; 
    } 
}

在Controller中获取ApiKey

直接从HttpContext.Items里取出对象即可:

[Authorize(Policy = "ApiKey")]
[ApiController]
[Route("api/[controller]")]
public class YourController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        // 安全地取出ApiKey对象
        if(HttpContext.Items.TryGetValue("ValidApiKey", out var apiKeyObj) && apiKeyObj is ApiKey apiKey)
        {
            // 现在可以直接使用ApiKey的所有属性了
            var userId = apiKey.uid;
            var isAdmin = apiKey.isadmin;
            var isPaid = apiKey.ispaid;
            
            // 示例业务逻辑
            return Ok($"当前APIKey所属用户ID:{userId},是否为管理员:{isAdmin}");
        }
        return BadRequest("无法获取有效的APIKey信息");
    }
}

方案二:添加到ClaimsPrincipal(适合和身份认证结合)

如果你的系统需要和ASP.NET Core身份认证体系结合,或者需要在多个地方(比如过滤器、日志)访问ApiKey的属性,把这些属性转成Claim加到context.User里会更合适。

修改你的ApiKeyRequirementHandler

在校验通过后,创建ClaimsIdentity并添加到用户主体:

protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ApiKeyRequirement requirement)
{
    // ...前面的Header解析和查询逻辑和方案一一致

    if(foundKey != null)
    {
        // 将ApiKey的属性转为Claim
        var claims = new List<Claim>
        {
            new Claim("ApiKeyId", foundKey.apikeyid),
            new Claim("Uid", foundKey.uid),
            new Claim(ClaimTypes.Role, foundKey.isadmin ? "Admin" : foundKey.ismod ? "Mod" : "User"),
            new Claim("IsActive", foundKey.isactive.ToString()),
            new Claim("IsPaid", foundKey.ispaid.ToString())
        };

        // 添加自定义身份认证标识
        var identity = new ClaimsIdentity(claims, "ApiKeyAuthentication");
        context.User.AddIdentity(identity);

        context.Succeed(requirement);
    }

    return Task.CompletedTask;
}

在Controller中获取Claims

通过User.Claims读取属性:

[Authorize(Policy = "ApiKey")]
[ApiController]
[Route("api/[controller]")]
public class YourController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        var userId = User.FindFirstValue("Uid");
        var isAdmin = User.IsInRole("Admin");
        var isPaid = bool.Parse(User.FindFirstValue("IsPaid"));
        
        return Ok($"用户ID:{userId},是否付费:{isPaid},是否管理员:{isAdmin}");
    }
}

小建议

  • 方案一更适合仅在Controller内使用ApiKey对象的场景,直接传递整个对象更便捷;
  • 方案二更适合需要在身份认证体系内共享ApiKey信息的场景,比如权限判断、全局日志等;
  • 记得在生产环境中,对APIKey的查询添加索引,提升数据库查询效率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:44:07