在 ASP.NET Core 中使用 WS 联合身份验证对用户进行身份验证

本教程演示如何使用户能够使用 WS 联合身份验证提供程序登录,例如 Active Directory 联合身份验证服务 (ADFS) 或 Microsoft Entra ID。 它使用 Facebook、Google 和外部提供程序身份验证中描述的 ASP.NET Core 示例应用。

对于 ASP.NET Core 应用,WS 联合身份验证支持由 Microsoft.AspNetCore.Authentication.WsFederation 提供。 此组件从 Microsoft.Owin.Security.WsFederation 移植而来,并共享该组件的许多机制。 但是,这两个组件在几个重要方面有所不同。

默认情况下,新的中间件:

  • 不允许未经请求的登录。 WS 联合身份验证协议的此功能容易受到 XSRF 攻击。 但是,可以使用 AllowUnsolicitedLogins 选项启用此功能。
  • 不检查每个表单发布中的登录消息。 仅检查对 CallbackPath 的登录请求。CallbackPath 默认为 /signin-wsfed,但可以通过 WsFederationOptions 类的继承 RemoteAuthenticationOptions.CallbackPath 属性进行更改。 通过启用 SkipUnrecognizedRequests 选项,可以与其他身份验证提供程序共享此路径。

将应用注册到 Active Directory

Active Directory 联合身份验证服务

  • 从 ADFS 管理控制台打开服务器的“添加信赖方信任向导”

Add Relying Party Trust Wizard: Welcome

  • 选择手动输入数据:

Add Relying Party Trust Wizard: Select Data Source

Add Relying Party Trust Wizard: Configure Certificate

  • 使用应用 URL 启用对 WS 联合身份验证被动协议的支持。 验证应用的端口是否正确:

Add Relying Party Trust Wizard: Configure URL

注意

端口必须是 HTTPS URL。 在开发期间托管应用时,IIS Express 可以提供自签名证书。 Kestrel 需要手动配置证书。 有关更多详细信息,请参阅 Kestrel 文档

  • 在向导的其余部分中单击“下一步”,最后单击“关闭”

  • ASP.NET Core Identity 需要“名称 ID”声明。 从“编辑声明规则”对话框添加一个:

Edit Claim Rules

  • 在“添加转换声明规则向导”中,保留默认模板“以声明方式发送 LDAP 属性”的选中状态,然后单击“下一步”。 添加将 SAM-Account-Name LDAP 属性映射到“名称 ID”传出声明的规则:

Add Transform Claim Rule Wizard: Configure Claim Rule

  • 在“编辑声明规则”窗口中单击“完成”>“确定”

Microsoft Entra ID

  • 导航到 Microsoft Entra ID 租户的“应用注册”边栏选项卡。 单击“新建应用程序注册”

Microsoft Entra ID: App registrations

  • 输入应用注册的名称。 该名称对 ASP.NET Core 应用并不重要。
  • 输入应用侦听的 URL 作为“登录 URL”

Microsoft Entra ID: Create app registration

  • 单击“终结点”并记下联合元数据文档 URL。 这是 WS 联合身份验证中间件的 MetadataAddress

Microsoft Entra ID: Endpoints

  • 导航到新的应用注册。 单击“公开 API”。 单击“应用程序 ID URI”>“设置”>“保存”。 记下应用程序 ID URI。 这是 WS 联合身份验证中间件的 Wtrealm

Microsoft Entra ID: App registration properties

在没有 ASP.NET Core Identity 的情况下使用 WS 联合身份验证

可以在没有 Identity 的情况下使用 WS 联合身份验证中间件。 例如:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
    })
     .AddWsFederation(options =>
     {
         options.Wtrealm = Configuration["wsfed:realm"];
         options.MetadataAddress = Configuration["wsfed:metadata"];
     })
     .AddCookie();

    services.AddControllersWithViews();
    services.AddRazorPages();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
        endpoints.MapRazorPages();
    });
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
    })
    .AddWsFederation(options =>
    {
        options.Wtrealm = Configuration["wsfed:realm"];
        options.MetadataAddress = Configuration["wsfed:metadata"];
    })
    .AddCookie();

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();

    app.UseAuthentication();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

添加 WS 联合身份验证作为 ASP.NET Core Identity 的外部登录提供程序

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();


    services.AddAuthentication()
        .AddWsFederation(options =>
        {
            // MetadataAddress represents the Active Directory instance used to authenticate users.
            options.MetadataAddress = "https://<ADFS FQDN or AAD tenant>/FederationMetadata/2007-06/FederationMetadata.xml";

            // Wtrealm is the app's identifier in the Active Directory instance.
            // For ADFS, use the relying party's identifier, its WS-Federation Passive protocol URL:
            options.Wtrealm = "https://localhost:44307/";

            // For AAD, use the Application ID URI from the app registration's Overview blade:
            options.Wtrealm = "api://bbd35166-7c13-49f3-8041-9551f2847b69";
        });

    services.AddControllersWithViews();
    services.AddRazorPages();
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDefaultIdentity<IdentityUser>()
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddAuthentication()
        .AddWsFederation(options =>
        {
            // MetadataAddress represents the Active Directory instance used to authenticate users.
            options.MetadataAddress = "https://<ADFS FQDN or AAD tenant>/FederationMetadata/2007-06/FederationMetadata.xml";

            // Wtrealm is the app's identifier in the Active Directory instance.
            // For ADFS, use the relying party's identifier, its WS-Federation Passive protocol URL:
            options.Wtrealm = "https://localhost:44307/";

            // For AAD, use the Application ID URI from the app registration's Overview blade:
            options.Wtrealm = "api://bbd35166-7c13-49f3-8041-9551f2847b69";
        });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

AddAuthentication(IServiceCollection, String) 重载设置 DefaultScheme 属性。 通过 AddAuthentication(IServiceCollection, Action<AuthenticationOptions>) 重载可配置身份验证选项,这些选项可用于为不同的目的设置默认身份验证方案。 对 AddAuthentication 的后续调用会替代之前配置的 AuthenticationOptions 属性。

注册身份验证处理程序的 AuthenticationBuilder 扩展方法只能在每个身份验证方案中调用一次。 存在允许配置方案属性、方案名称和显示名称的重载。

使用 WS 联合身份验证登录

浏览到应用并单击导航标头中的“登录”链接。 有一个使用 WsFederation 登录的选项:Log in page

使用 ADFS 作为提供程序时,该按钮会重定向到 ADFS 登录页面:ADFS sign-in page

使用 Microsoft Entra ID 作为提供程序,该按钮会重定向到 Microsoft Entra ID 登录页面:Microsoft Entra ID sign-in page

新用户成功登录会重定向到应用的用户注册页面:Register page