Angular+ASP.NET MVC集成ADFS SSO:Challenge()无法打开ADFS页面
问题分析与解决方案
你的核心问题在于AJAX请求无法处理服务器返回的302重定向响应,同时结合代码和配置来看,还有几处细节需要调整,下面逐个拆解:
1. 为什么Authentication.Challenge()无法打开ADFS页面?
当Angular通过AJAX(HttpClient.post)调用IsLogedInRoled接口时,服务器执行Challenge()后会返回302重定向到ADFS登录页,但AJAX请求不会自动跳转到这个重定向地址——它只会把重定向的响应内容(ADFS登录页HTML)当成接口返回值,而非触发浏览器跳转。这是导致你看不到ADFS页面的根本原因。
解决方案:让前端触发浏览器跳转,而非在API内处理Challenge
修改API逻辑,仅返回认证状态,把触发SSO跳转的逻辑交给前端:
[HttpPost] public bool IsLogedInRoled(NR role) { if (User.Identity.IsAuthenticated) { if (!string.IsNullOrEmpty(role.role)) { var isLogedInRoled = GetUserRoles().Select(x => x.ToLower()).Contains(role.role); return isLogedInRoled; } return true; } // 不再在这里执行Challenge,仅返回未认证状态 return false; }
然后新建一个MVC控制器的Action专门处理SSO跳转(让浏览器直接请求这个地址,而非AJAX):
public class AccountController : Controller { public ActionResult Login(string returnUrl = "/") { if (!User.Identity.IsAuthenticated) { HttpContext.GetOwinContext().Authentication.Challenge( new AuthenticationProperties { RedirectUri = returnUrl }, WsFederationAuthenticationDefaults.AuthenticationType); return new HttpUnauthorizedResult(); } return Redirect(returnUrl); } }
2. 调整Angular路由守卫的逻辑
当前AuthGuardService的异步处理存在问题,且未正确触发SSO跳转。修改为:
import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; import { AuthService } from 'app/services/auth/auth.service'; import { Observable, of } from 'rxjs'; import { map, catchError } from 'rxjs/operators'; @Injectable() export class AuthGuardService { canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> { return this.auth.checkLogedinRole(route.data) .pipe( map(isAuthenticated => { if (isAuthenticated) { return true; } // 直接跳转到MVC的Login Action,触发ADFS SSO window.location.href = `/Account/Login?returnUrl=${encodeURIComponent(state.url)}`; return false; }), catchError(() => { window.location.href = `/Account/Login?returnUrl=${encodeURIComponent(state.url)}`; return of(false); }) ); } constructor(private router: Router, private auth: AuthService) { } }
3. 优化Owin中间件配置
你的Startup.cs中中间件顺序和细节可以调整,确保Umbraco和自定义认证逻辑不冲突:
public class CustomeStartup : UmbracoDefaultOwinStartup { private static string realm = ConfigurationManager.AppSettings["ida:Wtrealm"]; private static string adfsMetadata = ConfigurationManager.AppSettings["ida:ADFSMetadata"]; private static string adfsWreply = ConfigurationManager.AppSettings["ida:Wreply"]; public override void Configuration(IAppBuilder app) { // 先加载Umbraco的默认Owin配置 base.Configuration(app); // 配置Cookie认证 app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); app.UseCookieAuthentication(new CookieAuthenticationOptions { CookieName = "E-services", // 可选:设置Cookie的过期时间、安全属性等 ExpireTimeSpan = TimeSpan.FromHours(8), CookieSecure = CookieSecureOption.Always }); // 配置WsFederation认证 app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions { Wtrealm = realm, MetadataAddress = adfsMetadata, Notifications = new WsFederationAuthenticationNotifications() { SecurityTokenValidated = context => { ClaimsIdentity identity = context.AuthenticationTicket.Identity; string[] roles = NParser.ToDecimal(identity.Name) > 0 ? new[] { "Student" } : new[] { "Employee" }; identity.AddClaim(new Claim(ClaimTypes.Role, roles.First())); return Task.CompletedTask; // 用更规范的Task.CompletedTask代替Task.FromResult(0) }, RedirectToIdentityProvider = context => { context.ProtocolMessage.Wreply = adfsWreply; return Task.CompletedTask; } }, }); app.UseStageMarker(PipelineStage.Authenticate); } }
4. 验证ADFS信赖方配置
确保ADFS服务器上的信赖方设置与你的Web.config一致:
ida:Wtrealm必须和ADFS信赖方的标识符完全匹配(包括末尾的斜杠)ida:Wreply必须在ADFS信赖方的允许回复URL列表中- 确保服务器能正常访问ADFS元数据地址(
ida:ADFSMetadata)
内容的提问来源于stack exchange,提问作者Wassan
相关产品推荐
相关产品推荐

