基于MSAL.NET与Microsoft Graph API的WPF应用SSO认证后自动关闭localhost标签页
解决WPF应用MSAL认证后自动关闭浏览器标签页的问题
当使用系统浏览器(WithUseEmbeddedWebView(false))进行MSAL认证时,要自动关闭认证完成后的localhost标签页,可以通过自定义本地HTTP监听器返回自动关闭页面的方式实现,具体步骤如下:
1. 修改Azure应用注册的重定向URI
将Azure门户中应用注册的重定向URI改为一个带具体端口的本地地址,比如 http://localhost:5000/redirect,确保和代码中配置的地址完全一致。
2. 实现本地HTTP监听器
在认证流程启动前,启动一个HttpListener监听指定地址,当收到AAD的重定向请求时,返回一段包含自动关闭窗口脚本的HTML页面。
3. 修正并调整MSAL认证代码
移除代码中重复的.ExecuteAsync();错误,配置正确的重定向URI,并在认证完成后停止HTTP监听器避免资源泄漏。
完整示例代码
private HttpListener _httpListener; private async void Button_Click(object sender, RoutedEventArgs e) { // 1. 启动本地HTTP监听器 var redirectUri = "http://localhost:5000/redirect"; _httpListener = new HttpListener(); _httpListener.Prefixes.Add(redirectUri + "/"); _httpListener.Start(); // 异步处理重定向请求 _ = HandleRedirectRequest(); // 2. 初始化MSAL应用 _app = PublicClientApplicationBuilder .Create(ClientId) .WithRedirectUri(redirectUri) .WithAuthority(Authority) .Build(); AuthenticationResult authResult = null; var accounts = await _app.GetAccountsAsync(); IAccount account = accounts.FirstOrDefault(); IEnumerable<string> scopes = new string[] { "User.Read", "openid" }; try { authResult = await _app.AcquireTokenSilent(scopes, account).ExecuteAsync(); } catch (MsalUiRequiredException ex) { // 修正原代码中重复的ExecuteAsync()错误 authResult = await _app.AcquireTokenInteractive(scopes) .WithUseEmbeddedWebView(false) .ExecuteAsync(); } // 3. 停止并关闭HTTP监听器 _httpListener.Stop(); _httpListener.Close(); // 后续Graph API调用逻辑 BaseBearerTokenAuthenticationProvider accessTokenProvider = new BaseBearerTokenAuthenticationProvider(new TokenProvider(authResult.AccessToken)); GraphServiceClient graphServiceClient = new GraphServiceClient(accessTokenProvider); User result = await graphServiceClient.Me.GetAsync(); GroupCollectionResponse g = await graphServiceClient.Groups.GetAsync(); DisplayBasicTokenInfo(authResult); DisplayGroupInformation(g); } private async Task HandleRedirectRequest() { while (_httpListener.IsListening) { var context = await _httpListener.GetContextAsync(); // 返回自动关闭窗口的HTML页面 string responseHtml = @" <html> <head> <script type='text/javascript'> window.close(); </script> </head> <body> 认证已完成,窗口将自动关闭... </body> </html>"; byte[] buffer = Encoding.UTF8.GetBytes(responseHtml); context.Response.ContentLength64 = buffer.Length; context.Response.ContentType = "text/html"; await context.Response.OutputStream.WriteAsync(buffer, 0, buffer.Length); context.Response.Close(); } }
注意事项
- 确保所选本地端口未被其他应用占用,可根据实际情况修改端口号。
- 若使用.NET Core/.NET 5+环境,需确认已引入
System.Net.HttpListener相关依赖。 - 认证完成后必须停止并关闭
HttpListener,避免不必要的资源占用。
内容的提问来源于stack exchange,提问作者Himanshu Pal
相关产品推荐
相关产品推荐

