如何中断MS Identity身份验证管道,校验用户是否在库并拒绝未授权访问
问题描述
我在Web应用中使用MS Active Directory进行身份验证、调用MS Graph服务,但数据库仅包含Azure Active Directory中的部分用户。由于无法控制该应用的AD权限与策略,希望依赖自有数据库进行权限管控。目前我在登录流程中通过事件,依据AD令牌声明中的邮箱查询数据库获取UserID并存入API使用(避免前端存储被篡改)。但当数据库查询无结果(即邮箱不在库中)时,我希望撤销访问令牌并将用户重定向至无权限提示页面。请问:
- 这是否可行?
- 此时Cookie/令牌是否已下发并赋予用户全应用访问权限?
- 是否有更适合的事件用于先执行数据库校验?
附上当前program.cs中的认证管道代码:
//authentication pipeline builder.Services.AddHttpContextAccessor(); var initialScopes = builder.Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' '); builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(options => { builder.Configuration.Bind("AzureAd", options); options.Events = new OpenIdConnectEvents { //Tap into this event to add a UserID Claim to a new HttpContext identity OnTokenValidated = context => { //This query returns the UserID from the DB by sending the email address in the claim from Azure AD string query = "select dbo.A2F_0013_ReturnUserIDForEmail(@Email) as UserID"; string connectionString = builder.Configuration.GetValue<string>("ConnectionStrings:DBContext"); string signInEmailAddress = context.Principal.FindFirstValue("preferred_username"); using (var connection = new SqlConnection(connectionString)) { var queryResult = connection.QueryFirst(query, new { Email = signInEmailAddress }); var claims = new List<Claim> { new Claim("UserID", queryResult.UserID.ToString()) }; var appIdentity = new ClaimsIdentity(claims); context.Principal.AddIdentity(appIdentity); } return Task.CompletedTask; }, //The SPA does not know when user session expires so this returns a 401 error for front end to use rather than a 302 redirect to MS login which axios cannot handle OnRedirectToIdentityProvider = context => { if (context.Request.Path.StartsWithSegments("/Account") || context.Request.Path.StartsWithSegments("/Base") || context.Request.Path.StartsWithSegments("/Dashboard")) { if (context.Response.StatusCode == (int)HttpStatusCode.OK) { context.Response.StatusCode = 403; context.Response.Headers.Add("ExpiredTokenRedirectUri", builder.Configuration.GetValue<string>("AzureAd:ExpiredTokenRedirectUri")); } context.HandleResponse(); } return Task.CompletedTask; } }; }).EnableTokenAcquisitionToCallDownstreamApi(initialScopes) .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi")) .AddInMemoryTokenCaches();
解答
1. 撤销令牌并重定向完全可行
在OnTokenValidated事件中,若数据库查询无结果,可通过context.Fail()方法终止认证流程,并手动构建响应重定向到无权限页面,直接阻止用户完成登录。
2. 此时Cookie/令牌尚未下发
OnTokenValidated事件的触发时机是:Azure AD的令牌已验证通过,但本地会话Cookie(ASP.NET Core的身份认证Cookie)尚未生成和下发。此时用户还未获得应用的访问权限,不会出现“已赋予全应用访问权”的情况。
3. OnTokenValidated已是最优事件选择
这个事件刚好卡在AD身份验证完成、本地会话创建之前,非常适合执行自定义的权限校验(比如数据库用户存在性检查)。无需更换其他事件,只需在现有逻辑中添加校验失败的处理即可。
修改后的代码示例
在OnTokenValidated中添加无用户时的处理逻辑:
OnTokenValidated = context => { string query = "select dbo.A2F_0013_ReturnUserIDForEmail(@Email) as UserID"; string connectionString = builder.Configuration.GetValue<string>("ConnectionStrings:DBContext"); string signInEmailAddress = context.Principal.FindFirstValue("preferred_username"); using (var connection = new SqlConnection(connectionString)) { // 使用QueryFirstOrDefault避免无结果时抛出异常 var queryResult = connection.QueryFirstOrDefault(query, new { Email = signInEmailAddress }); if (queryResult == null || queryResult.UserID == null) { // 终止认证流程 context.Fail("用户不在授权列表中"); // 构建重定向响应,替换为你的无权限页面路径 context.Response.Redirect("/Unauthorized"); context.HandleResponse(); return Task.CompletedTask; } var claims = new List<Claim> { new Claim("UserID", queryResult.UserID.ToString()) }; var appIdentity = new ClaimsIdentity(claims); context.Principal.AddIdentity(appIdentity); } return Task.CompletedTask; },
说明:
- 使用
QueryFirstOrDefault替代QueryFirst,避免无匹配用户时抛出未处理异常 - 校验失败时调用
context.Fail()标记认证失败,同时通过context.Response.Redirect()指定无权限页面路径,最后用context.HandleResponse()终止后续流程
内容的提问来源于stack exchange,提问作者Qiuzman
相关产品推荐
相关产品推荐

