ASP.NET Core 2中如何验证认证用户特定声明与路由段匹配用户ID?
嘿,刚好在ASP.NET Core 2里做过类似的权限验证需求,给你分享两种靠谱的实现方式,都能完美满足你要的「检查指定声明+路由ID匹配用户ID」的逻辑:
方案一:自定义授权属性(推荐,贴合身份验证体系)
这种方式继承自官方的AuthorizeAttribute,能和ASP.NET Core的授权系统深度集成,适合权限类的验证场景。
1. 实现自定义授权属性
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using System.Security.Claims; public class AuthorizeOwnResourceAttribute : AuthorizeAttribute, IAuthorizationFilter { // 需要检查的声明类型,以及路由中ID参数的名称(默认是"id") private readonly string _requiredClaimType; private readonly string _routeIdParamName; public AuthorizeOwnResourceAttribute(string requiredClaimType, string routeIdParamName = "id") { _requiredClaimType = requiredClaimType; _routeIdParamName = routeIdParamName; } public void OnAuthorization(AuthorizationFilterContext context) { var user = context.HttpContext.User; // 第一步:检查用户是否已认证 if (!user.Identity.IsAuthenticated) { context.Result = new UnauthorizedResult(); return; } // 第二步:检查用户是否拥有指定声明 if (!user.HasClaim(claim => claim.Type == _requiredClaimType)) { context.Result = new ForbidResult(); return; } // 第三步:获取路由中的ID参数 if (!context.RouteData.Values.TryGetValue(_routeIdParamName, out var routeIdObj) || routeIdObj == null) { context.Result = new BadRequestObjectResult("请求路由缺少必要的ID参数"); return; } var routeId = routeIdObj.ToString(); // 获取当前用户的ID(替换成你JWT中实际存储用户ID的Claim类型,比如自定义的"userId") var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; // 第四步:验证路由ID和用户ID是否匹配 if (string.IsNullOrEmpty(userId) || !string.Equals(routeId, userId, StringComparison.OrdinalIgnoreCase)) { context.Result = new ForbidResult(); return; } } }
2. 在Action上使用
直接把这个特性标记在需要验证的Action上即可:
[HttpPut("{id}")] // 替换成你实际需要检查的声明类型,比如"CanManageAccount" [AuthorizeOwnResource("CanManageAccount")] public IActionResult UpdateAccount(string id, [FromBody] AccountUpdateDto model) { // 你的业务逻辑代码 return Ok("更新成功"); }
方案二:自定义Action过滤器(灵活度更高)
如果不需要和授权系统深度绑定,只是想做请求前的参数+权限校验,用Action过滤器会更灵活。
1. 实现自定义Action过滤器
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using System.Security.Claims; public class ValidateOwnResourceFilterAttribute : ActionFilterAttribute { private readonly string _requiredClaimType; private readonly string _routeIdParamName; public ValidateOwnResourceFilterAttribute(string requiredClaimType, string routeIdParamName = "id") { _requiredClaimType = requiredClaimType; _routeIdParamName = routeIdParamName; } public override void OnActionExecuting(ActionExecutingContext context) { var user = context.HttpContext.User; // 检查认证状态 if (!user.Identity.IsAuthenticated) { context.Result = new UnauthorizedResult(); return; } // 检查指定声明 if (!user.HasClaim(claim => claim.Type == _requiredClaimType)) { context.Result = new ForbidResult(); return; } // 获取路由ID(也可以从Action参数中获取:context.ActionArguments.TryGetValue(_routeIdParamName, out var id)) var routeId = context.RouteData.Values[_routeIdParamName]?.ToString(); if (string.IsNullOrEmpty(routeId)) { context.Result = new BadRequestObjectResult("缺少必要的ID路由参数"); return; } // 获取用户ID并匹配 var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (!string.Equals(routeId, userId, StringComparison.OrdinalIgnoreCase)) { context.Result = new ForbidResult(); return; } base.OnActionExecuting(context); } }
2. 使用方式
和授权属性的用法完全一致:
[HttpPut("{id}")] [ValidateOwnResourceFilter("CanManageAccount")] public IActionResult UpdateAccount(string id, [FromBody] AccountUpdateDto model) { // 业务逻辑 return Ok(); }
一些关键注意事项
- 替换Claim类型:把代码中的
ClaimTypes.NameIdentifier替换成你JWT令牌中实际存储用户ID的Claim类型(比如你自定义的"userId") - 声明类型适配:
"CanManageAccount"要换成你系统中实际需要检查的声明名称,确保JWT里包含这个声明 - 全局注册(可选):如果大部分带ID的路由都需要这个验证,可以在
Startup.cs中全局注册过滤器:
services.AddMvc(options => { options.Filters.Add(new ValidateOwnResourceFilterAttribute("CanManageAccount")); });
不过全局注册后,记得给不需要验证的Action加上[AllowAnonymous]或者自定义的跳过特性。
内容的提问来源于stack exchange,提问作者Pure.Krome
相关产品推荐
相关产品推荐

