.NET Web API中扩展Authorize属性实现令牌验证及用户注入
解决方案:扩展ASP.NET Core授权流程实现自动令牌验证与用户注入
针对你的需求,我们可以通过自定义授权处理程序和动作过滤器来扩展ASP.NET Core的授权流程,实现策略验证后自动执行令牌校验、用户数据库验证,并将合法用户注入到请求上下文供后续使用,彻底消除控制器中的重复代码。
一、自定义授权处理程序:集成令牌与用户验证
创建自定义的授权需求和处理程序,在策略验证通过后自动执行令牌校验和用户查询,并将合法用户存入请求上下文。
1. 定义授权需求(若原有策略未关联自定义需求)
// 定义Admin角色的授权需求标识 public class AdminRequirement : IAuthorizationRequirement { }
2. 实现自定义授权处理程序
public class AdminAuthorizationHandler : AuthorizationHandler<AdminRequirement> { private readonly UserManager<ApplicationUser> _userManager; private readonly IConfiguration _configuration; private readonly ILogger<AdminAuthorizationHandler> _logger; private readonly IStringLocalizer _localizer; private readonly IActionContextAccessor _actionContextAccessor; public AdminAuthorizationHandler( UserManager<ApplicationUser> userManager, IConfiguration configuration, ILogger<AdminAuthorizationHandler> logger, IStringLocalizer localizer, IActionContextAccessor actionContextAccessor) { _userManager = userManager; _configuration = configuration; _logger = logger; _localizer = localizer; _actionContextAccessor = actionContextAccessor; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, AdminRequirement requirement) { // 1. 先确保基础身份验证通过 if (!context.User.Identity.IsAuthenticated) { context.Fail(); return; } // 2. 获取HttpContext上下文 if (context.Resource is not HttpContext httpContext) { context.Fail(); return; } // 3. 获取Controller上下文用于ModelState校验 var controllerContext = _actionContextAccessor.ActionContext as ControllerContext; if (controllerContext == null) { context.Fail(); return; } // 4. 执行令牌验证与用户查询(复用原有Helper逻辑) var (success, user) = await UserHelpers.ValidateTokenAndGetUserAsync( httpContext, _userManager, _configuration, _logger, controllerContext, controllerContext.ModelState); if (!success || user?.CustomerId == null) { httpContext.Items["AuthErrorMsg"] = _localizer[ResourceConstants.Unauthorized.UserNotFoundError].Value; context.Fail(); return; } // 5. 将合法用户存入HttpContext,供后续使用 httpContext.Items["AuthenticatedUser"] = user; // 6. 授权通过 context.Succeed(requirement); } }
二、注册授权服务与策略配置
在Program.cs中注册自定义处理程序,并将其关联到原有"Admin"策略:
// 注册IActionContextAccessor用于获取控制器上下文 builder.Services.AddSingleton<IActionContextAccessor, ActionContextAccessor>(); // 配置授权策略 builder.Services.AddAuthorization(options => { options.AddPolicy("Admin", policy => { // 保留原有策略规则(比如角色要求) policy.RequireRole("Admin"); // 添加自定义授权需求 policy.Requirements.Add(new AdminRequirement()); }); }); // 注册自定义授权处理程序 builder.Services.AddScoped<IAuthorizationHandler, AdminAuthorizationHandler>();
三、自定义动作过滤器:自动更新用户Cookie
将原有CookieToResponse逻辑封装为动作过滤器,在请求处理完成后自动执行:
public class UpdateUserCookieFilter : IAsyncActionFilter { private readonly IConfiguration _configuration; private readonly IWebHostEnvironment _webHostEnvironment; public UpdateUserCookieFilter(IConfiguration configuration, IWebHostEnvironment webHostEnvironment) { _configuration = configuration; _webHostEnvironment = webHostEnvironment; } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // 先执行控制器方法 var resultContext = await next(); // 仅当授权成功且请求无异常时更新Cookie if (context.HttpContext.Items.TryGetValue("AuthenticatedUser", out var userObj) && userObj is ApplicationUser user) { await UserHelpers.CookieToResponse( user, _configuration, _webHostEnvironment, context.HttpContext.Response, context.HttpContext.Request) .ConfigureAwait(false); } } }
注册过滤器(可全局注册或按需添加到控制器):
builder.Services.AddControllers(options => { // 全局注册,所有授权请求自动执行Cookie更新 options.Filters.Add<UpdateUserCookieFilter>(); });
四、简化控制器方法
现在控制器方法无需手动验证令牌和用户,直接从上下文获取合法用户即可:
方式1:从HttpContext.Items直接获取
[Authorize(Policy = "Admin")] [Route("get")] [HttpGet] public async Task<IActionResult> Get() { // 兜底校验(理论上授权通过后必然存在) if (!HttpContext.Items.TryGetValue("AuthenticatedUser", out var userObj) || userObj is not ApplicationUser user) { var errorMsg = HttpContext.Items["AuthErrorMsg"] as string ?? "Unauthorized"; return Unauthorized(HttpResponseHelper.GetErrorResponse(StatusCodes.Status401Unauthorized, errorMsg)); } // 直接使用user对象执行业务逻辑 // ... return Ok(); }
方式2:自定义参数绑定(更简洁)
创建模型绑定器,直接将用户注入到方法参数:
public class AuthenticatedUserBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext.HttpContext.Items.TryGetValue("AuthenticatedUser", out var userObj) && userObj is ApplicationUser user) { bindingContext.Result = ModelBindingResult.Success(user); } else { bindingContext.Result = ModelBindingResult.Failed(); } return Task.CompletedTask; } } // 注册绑定器提供者 public class AuthenticatedUserBinderProvider : IModelBinderProvider { public IModelBinder? GetBinder(ModelBinderProviderContext context) { return context.Metadata.ModelType == typeof(ApplicationUser) ? new AuthenticatedUserBinder() : null; } }
在Program.cs中注册绑定器:
builder.Services.AddControllers(options => { options.ModelBinderProviders.Insert(0, new AuthenticatedUserBinderProvider()); options.Filters.Add<UpdateUserCookieFilter>(); });
控制器方法可简化为:
[Authorize(Policy = "Admin")] [Route("get")] [HttpGet] public async Task<IActionResult> Get(ApplicationUser user) { // 直接使用注入的user对象 // ... return Ok(); }
内容的提问来源于stack exchange,提问作者OJs
相关产品推荐
相关产品推荐

