ASP.NET MVC中带角色的Authorize特性跳转Access Denied问题排查
问题根本原因
你的问题出在中间件执行顺序错误,其次加角色声明的写法也有小瑕疵:
1. 核心问题:中间件顺序不符合执行逻辑
ASP.NET Core 中间件严格按照注册的先后顺序执行,你当前的中间件排列为:
app.UseAuthentication() → 读取认证信息构造HttpContext.User app.UseAuthorization() → 执行[Authorize]特性的权限校验 app.UseAddClaimsFromDb() → 从数据库读取角色,添加到User的声明中
也就是说,[Authorize]校验跑的时候,你还没有把Admin角色加到用户的声明里,自然校验失败跳转到AccessDenied页。你后面写的测试代码、还有视图里的User.IsInRole都是授权校验完成、进入控制器/视图执行阶段才跑的,这时候自定义中间件已经执行完,角色已经加上,所以返回true。
2. 次要问题:声明添加方式不规范
你新建了一个空的ClaimsIdentity来存角色声明,这个新Identity没有绑定认证类型,部分场景下可能出现身份不被识别的问题。
修复方案
第一步:调整中间件顺序
把自定义加角色的中间件挪到UseAuthentication之后,UseAuthorization之前,保证授权校验之前,角色已经被添加到用户声明中:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 省略其他前置中间件... app.UseRouting(); app.UseAuthentication(); // 先加自定义角色声明,再执行授权校验 app.UseAddClaimsFromDb(); app.UseAuthorization(); // 省略后续中间件... }
第二步:优化声明添加逻辑
直接把角色声明加到已经认证的主Identity上,不需要新建Identity:
public async Task InvokeAsync(HttpContext httpContext, EmpDBContext dBContext) { var user = httpContext.User; if(user != null && user.Identity.IsAuthenticated) { string email = user.Identity.Name; UserDetail userDetail = await dBContext.UserDetails.FirstOrDefaultAsync(e => e.EmailId == email); string role = userDetail?.Role ?? "Employee"; // 取当前已经过认证的Identity,直接添加角色声明 var authenticatedIdentity = user.Identities.First(i => i.IsAuthenticated); authenticatedIdentity.AddClaim(new Claim(ClaimTypes.Role, role)); } await next(httpContext); }
修改完成后,[Authorize(Roles = "Admin")]和自定义Policy的校验都会正常生效。
内容的提问来源于stack exchange,提问作者Sandeep Ranjan
相关产品推荐
相关产品推荐

