MVC集成Azure Entra ID认证:Pass方法重复执行及身份问题
问题修复方案
问题根源
Pass方法执行两次:Startup.cs中重复调用了services.AddControllersWithViews(),导致MVC授权相关服务被重复注册,请求处理时触发两次授权检查。- 存在两个身份:在授权需求中直接调用
AddIdentity追加自定义身份,而非替换或整合原有Azure AD身份,同时授权逻辑的执行时机不符合ASP.NET Core设计规范。
修复步骤
1. 移除重复的MVC服务注册
打开Startup.cs,删除重复的services.AddControllersWithViews()调用:
原代码:
services.AddControllersWithViews(); var mvcBuilder = services.AddControllersWithViews();
修改为:
var mvcBuilder = services.AddControllersWithViews();
2. 使用Claims Transformation处理用户身份
创建专门的声明转换类,负责从数据库获取用户信息并替换/扩展用户身份(这是ASP.NET Core中处理用户声明的标准方式,只会执行一次):
public class CustomClaimsTransformer : IClaimsTransformation { private readonly DataContext _dataContext; public CustomClaimsTransformer(DataContext dataContext) { _dataContext = dataContext; } public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal) { // 避免重复转换,检查是否已有自定义声明 if (principal.HasClaim(c => c.Type == ClaimTypes.Role && c.Issuer == "Custom")) { return principal; } var email = principal.Identity?.Name; if (string.IsNullOrEmpty(email)) { return principal; } var user = await _dataContext.Users .FirstOrDefaultAsync(x => x.Email == email && x.Active); if (user == null) { return principal; } // 创建自定义身份,使用原认证类型确保为主身份 var customIdentity = new ClaimsIdentity( new[] { new Claim(ClaimTypes.Email, user.Email, ClaimValueTypes.String, "Custom"), new Claim(ClaimTypes.Name, user.Name, ClaimValueTypes.String, "Custom"), new Claim(ClaimTypes.Role, user.Role, ClaimValueTypes.String, "Custom") }, principal.Identity.AuthenticationType); // 替换为自定义身份作为主身份(如需保留Azure AD声明,可添加原身份到新Principal) return new ClaimsPrincipal(customIdentity); } }
在Startup.cs的ConfigureServices中注册该类:
services.AddScoped<IClaimsTransformation, CustomClaimsTransformer>();
3. 简化授权策略
授权逻辑只需要验证用户是否合法,无需修改身份。可以直接通过声明检查实现,无需自定义PolicyRequirement和PolicyAuthorizationHandler:
修改Startup.cs中的授权策略配置:
services.AddAuthorization(options => { options.AddPolicy("AzureAccount", policyBuilder => { policyBuilder.RequireAuthenticatedUser(); // 检查是否存在自定义颁发的角色声明 policyBuilder.RequireClaim(ClaimTypes.Role, issuer: "Custom"); }); });
如果仍需保留自定义授权Handler,调整PolicyAuthorizationHandler仅做授权检查:
public class PolicyAuthorizationHandler : AuthorizationHandler<PolicyRequirement> { private readonly DataContext _dataContext; public PolicyAuthorizationHandler(DataContext dataContext) { _dataContext = dataContext; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PolicyRequirement requirement) { var email = context.User.Identity?.Name; if (string.IsNullOrEmpty(email)) { context.Fail(); return; } var userExists = await _dataContext.Users .AnyAsync(x => x.Email == email && x.Active); if (userExists) { context.Succeed(requirement); } else { context.Fail(); } } }
同时移除PolicyRequirement中的Pass方法,仅保留空的需求类:
public class PolicyRequirement : IAuthorizationRequirement { }
效果验证
完成上述修改后:
- 授权检查逻辑只会执行一次
_httpContextAccessor.HttpContext.User.Identity将是你自定义的身份- 用户身份不会出现重复的情况
内容的提问来源于stack exchange,提问作者Danny Verdel
相关产品推荐
相关产品推荐

