如何将AuthorizationHandler中的数据传递至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
相关产品推荐
相关产品推荐

