You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET 6 Blazor Hybrid应用中实现Azure AD登录认证?

为.NET 6 Blazor Hybrid应用集成Azure AD身份认证与授权

1. 安装依赖包

在项目中安装以下NuGet包:

  • Microsoft.Identity.Client:用于MSAL认证逻辑
  • Microsoft.AspNetCore.Components.Authorization:Blazor授权基础组件

2. 配置Azure AD注册应用

在Azure门户完成应用注册后,记录并配置以下信息:

  • 租户ID(TenantId)、客户端ID(ClientId)
  • 设置重定向URI:根据你的Blazor Hybrid平台选择对应格式:
    • MAUI:maui://com.yourcompany.yourapp
    • WPF/WinForms:http://localhost
  • 按需添加User.Read等Microsoft Graph权限(确保管理员同意必要权限)

3. 注册认证服务(Program.cs)

在Program.cs中配置MSAL客户端和自定义认证状态提供者:

using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Identity.Client;

var builder = WebApplication.CreateBuilder(args);

// 添加Blazor基础服务
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();

// 注册MSAL客户端
builder.Services.AddSingleton(sp =>
{
    var config = sp.GetRequiredService<IConfiguration>();
    return PublicClientApplicationBuilder
        .Create(config["AzureAd:ClientId"])
        .WithAuthority($"https://login.microsoftonline.com/{config["AzureAd:TenantId"]}")
        .WithRedirectUri(config["AzureAd:RedirectUri"])
        .Build();
});

// 注册自定义认证状态提供者与授权服务
builder.Services.AddScoped<AuthenticationStateProvider, AzureAdAuthStateProvider>();
builder.Services.AddAuthorizationCore();

var app = builder.Build();

// 配置中间件(静态文件、路由等)
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();

在appsettings.json中添加Azure AD配置节点:

{
  "AzureAd": {
    "TenantId": "你的租户ID",
    "ClientId": "你的客户端ID",
    "RedirectUri": "对应平台的重定向URI"
  }
}

4. 实现自定义AuthenticationStateProvider

创建AzureAdAuthStateProvider.cs类,处理登录、登出及用户状态同步:

using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Identity.Client;
using System.Security.Claims;

public class AzureAdAuthStateProvider : AuthenticationStateProvider
{
    private readonly IPublicClientApplication _msalClient;
    private IEnumerable<Claim> _currentUserClaims = Enumerable.Empty<Claim>();

    public AzureAdAuthStateProvider(IPublicClientApplication msalClient)
    {
        _msalClient = msalClient;
    }

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var accounts = await _msalClient.GetAccountsAsync();
        if (accounts.Any())
        {
            try
            {
                // 静默获取令牌(可替换为你需要的API权限范围)
                var result = await _msalClient.AcquireTokenSilent(new[] { "User.Read" }, accounts.First())
                    .ExecuteAsync();
                _currentUserClaims = result.ClaimsPrincipal.Claims;
            }
            catch (MsalUiRequiredException)
            {
                // 无有效会话,需交互式登录
                _currentUserClaims = Enumerable.Empty<Claim>();
            }
        }

        var identity = _currentUserClaims.Any() ? new ClaimsIdentity(_currentUserClaims, "AzureAD") : new ClaimsIdentity();
        return new AuthenticationState(new ClaimsPrincipal(identity));
    }

    public async Task LoginAsync()
    {
        try
        {
            var result = await _msalClient.AcquireTokenInteractive(new[] { "User.Read" })
                .ExecuteAsync();
            _currentUserClaims = result.ClaimsPrincipal.Claims;
            NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
        }
        catch (MsalException)
        {
            // 处理登录异常(如用户取消登录)
        }
    }

    public async Task LogoutAsync()
    {
        var accounts = await _msalClient.GetAccountsAsync();
        foreach (var account in accounts)
        {
            await _msalClient.RemoveAsync(account);
        }
        _currentUserClaims = Enumerable.Empty<Claim>();
        NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
    }
}

5. 在Blazor组件中使用认证与授权

登录/登出组件(LoginDisplay.razor)

@inject AzureAdAuthStateProvider AzureAdAuth

<AuthorizeView>
    <Authorized>
        <span>欢迎, @context.User.Identity.Name!</span>
        <button @onclick="AzureAdAuth.LogoutAsync" class="ml-2">登出</button>
    </Authorized>
    <NotAuthorized>
        <button @onclick="AzureAdAuth.LoginAsync">使用Azure AD登录</button>
    </NotAuthorized>
</AuthorizeView>

保护路由(App.razor)

用AuthorizeRouteView替换默认RouteView,实现路由级授权:

<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
            <NotAuthorized>
                <div class="p-4">
                    <p>你需要登录才能访问此页面。</p>
                    <LoginDisplay />
                </div>
            </NotAuthorized>
        </AuthorizeRouteView>
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
        <PageTitle>Not found</PageTitle>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>抱歉,未找到该页面。</p>
        </LayoutView>
    </NotFound>
</Router>

组件级授权

在需要保护的组件顶部添加[Authorize]特性:

@attribute [Authorize]

<h1>仅授权用户可见的内容</h1>
<p>此页面需要Azure AD登录才能访问。</p>

平台特定注意事项

  • MAUI:需在Android的AndroidManifest.xml中配置重定向URI的intent过滤器,在iOS的Info.plist中添加URL类型配置。
  • WPF/WinForms:确保重定向URI设置为http://localhost,并在应用启动时处理MSAL的回调(WPF可通过Dispatcher处理异步回调)。

内容的提问来源于stack exchange,提问作者Lukas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 20:27:06