- 概述
- 新增功能
- 基础知识
- 测试、调试和疑难解答
- 低版本升级迁移
将身份验证和标识迁移到 ASP.NET Core 2.0
作者: Scott Addie和Hao Kung
ASP.NET Core 2.0 具有新的身份验证和标识模型,可通过使用服务简化配置。 ASP.NET Core 1.x 应用程序使用身份验证或标识可以更新以使用新的模型,如下所述。
更新命名空间
在1.x 中,在 Microsoft.AspNetCore.Identity.EntityFrameworkCore
命名空间中找到了类 IdentityRole
和 IdentityUser
。
在2.0 中,Microsoft.AspNetCore.Identity 命名空间成为几个此类类的新宿主。 对于默认标识代码,受影响的类包括 ApplicationUser
和 Startup
。 调整 using
语句以解析受影响的引用。
身份验证中间件和服务
在1.x 项目中,通过中间件配置身份验证。 为要支持的每个身份验证方案调用中间件方法。
下面的1.x 示例在Startup.cs中配置具有标识的 Facebook 身份验证:
public void ConfigureServices(IServiceCollection services) { services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>(); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) { app.UseIdentity(); app.UseFacebookAuthentication(new FacebookOptions { AppId = Configuration["auth:facebook:appid"], AppSecret = Configuration["auth:facebook:appsecret"] }); }
在2.0 项目中,通过服务配置身份验证。 在Startup.cs的 ConfigureServices
方法中注册每个身份验证方案。 UseIdentity
方法将替换为 UseAuthentication
。
以下2.0 示例在Startup.cs中配置具有标识的 Facebook 身份验证:
public void ConfigureServices(IServiceCollection services) { services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>(); // If you want to tweak Identity cookies, they're no longer part of IdentityOptions. services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn"); services.AddAuthentication() .AddFacebook(options => { options.AppId = Configuration["auth:facebook:appid"]; options.AppSecret = Configuration["auth:facebook:appsecret"]; }); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) { app.UseAuthentication(); }
UseAuthentication
方法添加单个身份验证中间件组件,该组件负责自动身份验证和远程身份验证请求的处理。 它将所有单个中间件组件替换为一个公共中间件组件。
下面是每个主要身份验证方案的2.0 迁移说明。
基于 Cookie 的身份验证
选择以下两个选项之一,并在Startup.cs中进行必要的更改:
使用带有标识的 cookie
将
UseIdentity
替换为Configure
方法中的UseAuthentication
:app.UseAuthentication();
在
ConfigureServices
方法中调用AddIdentity
方法,以添加 cookie 身份验证服务。(可选)调用
ConfigureServices
方法中的ConfigureApplicationCookie
或ConfigureExternalCookie
方法以调整标识 cookie 设置。services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn");
使用没有标识的 cookie
用
UseAuthentication
替换Configure
方法中的UseCookieAuthentication
方法调用:app.UseAuthentication();
在
ConfigureServices
方法中调用AddAuthentication
和AddCookie
方法:// If you don't want the cookie to be automatically authenticated and assigned to HttpContext.User, // remove the CookieAuthenticationDefaults.AuthenticationScheme parameter passed to AddAuthentication. services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.LoginPath = "/Account/LogIn"; options.LogoutPath = "/Account/LogOff"; });
JWT 持有者身份验证
在Startup.cs中进行以下更改:
用
UseAuthentication
替换Configure
方法中的UseJwtBearerAuthentication
方法调用:app.UseAuthentication();
在
ConfigureServices
方法中调用AddJwtBearer
方法:services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Audience = "http://localhost:5001/"; options.Authority = "http://localhost:5000/"; });
此代码片段不使用标识,因此应通过将
JwtBearerDefaults.AuthenticationScheme
传递到AddAuthentication
方法来设置默认方案。
OpenID Connect (OIDC)身份验证
在Startup.cs中进行以下更改:
用
UseAuthentication
替换Configure
方法中的UseOpenIdConnectAuthentication
方法调用:app.UseAuthentication();
在
ConfigureServices
方法中调用AddOpenIdConnect
方法:services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect(options => { options.Authority = Configuration["auth:oidc:authority"]; options.ClientId = Configuration["auth:oidc:clientid"]; });
将
OpenIdConnectOptions
操作中的PostLogoutRedirectUri
属性替换为SignedOutRedirectUri
:.AddOpenIdConnect(options => { options.SignedOutRedirectUri = "https://contoso.com"; });
Facebook 身份验证
在Startup.cs中进行以下更改:
用
UseAuthentication
替换Configure
方法中的UseFacebookAuthentication
方法调用:app.UseAuthentication();
在
ConfigureServices
方法中调用AddFacebook
方法:services.AddAuthentication() .AddFacebook(options => { options.AppId = Configuration["auth:facebook:appid"]; options.AppSecret = Configuration["auth:facebook:appsecret"]; });
Google 身份验证
在Startup.cs中进行以下更改:
用
UseAuthentication
替换Configure
方法中的UseGoogleAuthentication
方法调用:app.UseAuthentication();
在
ConfigureServices
方法中调用AddGoogle
方法:services.AddAuthentication() .AddGoogle(options => { options.ClientId = Configuration["auth:google:clientid"]; options.ClientSecret = Configuration["auth:google:clientsecret"]; });
Microsoft 帐户身份验证
有关 Microsoft 帐户身份验证的详细信息,请参阅此 GitHub 问题。
在Startup.cs中进行以下更改:
用
UseAuthentication
替换Configure
方法中的UseMicrosoftAccountAuthentication
方法调用:app.UseAuthentication();
在
ConfigureServices
方法中调用AddMicrosoftAccount
方法:services.AddAuthentication() .AddMicrosoftAccount(options => { options.ClientId = Configuration["auth:microsoft:clientid"]; options.ClientSecret = Configuration["auth:microsoft:clientsecret"]; });
Twitter 身份验证
在Startup.cs中进行以下更改:
用
UseAuthentication
替换Configure
方法中的UseTwitterAuthentication
方法调用:app.UseAuthentication();
在
ConfigureServices
方法中调用AddTwitter
方法:services.AddAuthentication() .AddTwitter(options => { options.ConsumerKey = Configuration["auth:twitter:consumerkey"]; options.ConsumerSecret = Configuration["auth:twitter:consumersecret"]; });
设置默认身份验证方案
在1.x 中, AuthenticationOptions基类的 AutomaticAuthenticate
和 AutomaticChallenge
属性应在单个身份验证方案上进行设置。 没有正确的方法来强制执行此操作。
在2.0 中,已将这两个属性作为单独 AuthenticationOptions
实例的属性删除。 可以在Startup.cs的 ConfigureServices
方法中的 AddAuthentication
方法调用中配置它们:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
在上面的代码段中,默认方案设置为 CookieAuthenticationDefaults.AuthenticationScheme
("Cookie")。
或者,使用 AddAuthentication
方法的重载版本设置多个属性。 在下面的重载方法示例中,将默认方案设置为 CookieAuthenticationDefaults.AuthenticationScheme
。 可以在单独的 [Authorize]
属性或授权策略中指定身份验证方案。
services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; });
如果满足以下条件之一,则定义2.0 中的默认方案:
- 希望用户自动登录
- 在不指定方案的情况下使用
[Authorize]
属性或授权策略
此规则的例外情况是 AddIdentity
方法。 此方法为你添加 cookie,并将默认身份验证和质询方案设置为应用程序 cookie IdentityConstants.ApplicationScheme
。 此外,它还会将默认登录方案设置为外部 cookie IdentityConstants.ExternalScheme
。
使用 HttpContext 身份验证扩展
IAuthenticationManager
接口是到1.x 身份验证系统的主要入口点。 它已替换为 Microsoft.AspNetCore.Authentication
命名空间中的一组新的 HttpContext
扩展方法。
例如,1.x 项目引用 Authentication
属性:
// Clear the existing external cookie to ensure a clean login process await HttpContext.Authentication.SignOutAsync(_externalCookieScheme);
在2.0 项目中,导入 Microsoft.AspNetCore.Authentication
命名空间,并删除 Authentication
属性引用:
// Clear the existing external cookie to ensure a clean login process await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
Windows 身份验证(http.sys/IISIntegration)
Windows 身份验证有两种变体:
该主机仅允许经过身份验证的用户。 此变体不受2.0 更改的影响。
宿主允许匿名用户和经过身份验证的用户。 此变体受2.0 更改的影响。 例如,应用程序应允许IIS或http.sys层上的匿名用户,但在控制器级别对用户进行授权。 在此方案中,在
Startup.ConfigureServices
方法中设置默认方案。对于AspNetCore,请将默认方案设置为
IISDefaults.AuthenticationScheme
:using Microsoft.AspNetCore.Server.IISIntegration; services.AddAuthentication(IISDefaults.AuthenticationScheme);
对于AspNetCore,请将默认方案设置为
HttpSysDefaults.AuthenticationScheme
:using Microsoft.AspNetCore.Server.HttpSys; services.AddAuthentication(HttpSysDefaults.AuthenticationScheme);
未能设置默认方案会阻止授权(质询)请求使用以下例外:
System.InvalidOperationException
:未指定任何 authenticationScheme,并且找不到 DefaultChallengeScheme。
有关更多信息,请参见在 ASP.NET Core 中配置 Windows 身份验证。
IdentityCookieOptions 实例
2.0 更改的副作用是切换到使用命名选项而不是 cookie 选项实例。 将删除自定义标识 cookie 方案名称的功能。
例如,1.x 项目使用构造函数注入将 IdentityCookieOptions
参数传递到AccountController.cs和ManageController.cs中。 可从提供的实例中访问外部 cookie 身份验证方案:
public AccountController( UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, IOptions<IdentityCookieOptions> identityCookieOptions, IEmailSender emailSender, ISmsSender smsSender, ILoggerFactory loggerFactory) { _userManager = userManager; _signInManager = signInManager; _externalCookieScheme = identityCookieOptions.Value.ExternalCookieAuthenticationScheme; _emailSender = emailSender; _smsSender = smsSender; _logger = loggerFactory.CreateLogger<AccountController>(); }
上述构造函数注入在2.0 项目中变得不必要,可以删除 _externalCookieScheme
字段:
public AccountController( UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, IEmailSender emailSender, ISmsSender smsSender, ILoggerFactory loggerFactory) { _userManager = userManager; _signInManager = signInManager; _emailSender = emailSender; _smsSender = smsSender; _logger = loggerFactory.CreateLogger<AccountController>(); }
1.x 项目使用 _externalCookieScheme
字段,如下所示:
// Clear the existing external cookie to ensure a clean login process await HttpContext.Authentication.SignOutAsync(_externalCookieScheme);
在2.0 项目中,将前面的代码替换为以下代码。 可以直接使用 IdentityConstants.ExternalScheme
常量。
// Clear the existing external cookie to ensure a clean login process await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
导入以下命名空间,解析新添加的 SignOutAsync
调用:
using Microsoft.AspNetCore.Authentication;
添加 IdentityUser POCO 导航属性
已删除基 IdentityUser
POCO (普通旧 CLR 对象)的实体框架(EF)核心导航属性。 如果你的1.x 项目使用这些属性,请将其手动添加回2.0 项目:
/// <summary> /// Navigation property for the roles this user belongs to. /// </summary> public virtual ICollection<IdentityUserRole<int>> Roles { get; } = new List<IdentityUserRole<int>>(); /// <summary> /// Navigation property for the claims this user possesses. /// </summary> public virtual ICollection<IdentityUserClaim<int>> Claims { get; } = new List<IdentityUserClaim<int>>(); /// <summary> /// Navigation property for this users login accounts. /// </summary> public virtual ICollection<IdentityUserLogin<int>> Logins { get; } = new List<IdentityUserLogin<int>>();
若要防止重复的外键,运行 EF Core 迁移时,将以下代码添加到你IdentityDbContext
类的OnModelCreating
方法 (后base.OnModelCreating();
调用):
protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); // Customize the ASP.NET Core Identity model and override the defaults if needed. // For example, you can rename the ASP.NET Core Identity table names and more. // Add your customizations after calling base.OnModelCreating(builder); builder.Entity<ApplicationUser>() .HasMany(e => e.Claims) .WithOne() .HasForeignKey(e => e.UserId) .IsRequired() .OnDelete(DeleteBehavior.Cascade); builder.Entity<ApplicationUser>() .HasMany(e => e.Logins) .WithOne() .HasForeignKey(e => e.UserId) .IsRequired() .OnDelete(DeleteBehavior.Cascade); builder.Entity<ApplicationUser>() .HasMany(e => e.Roles) .WithOne() .HasForeignKey(e => e.UserId) .IsRequired() .OnDelete(DeleteBehavior.Cascade); }
替换 GetExternalAuthenticationSchemes
已删除同步方法 GetExternalAuthenticationSchemes
以支持异步版本。 1.x 项目的控制器/ManageController中包含以下代码:
var otherLogins = _signInManager.GetExternalAuthenticationSchemes().Where(auth => userLogins.All(ul => auth.AuthenticationScheme != ul.LoginProvider)).ToList();
此方法也出现在Views/Account/Login 中。 cshtml :
@{ var loginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList(); if (loginProviders.Count == 0) { <div> <p> There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a> for details on setting up this ASP.NET application to support logging in via external services. </p> </div> } else { <form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal"> <div> <p> @foreach (var provider in loginProviders) { <button type="submit" class="btn btn-default" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.AuthenticationScheme</button> } </p> </div> </form> } }
在2.0 项目中,使用 GetExternalAuthenticationSchemesAsync 方法。 ManageController.cs中的更改类似于以下代码:
var schemes = await _signInManager.GetExternalAuthenticationSchemesAsync(); var otherLogins = schemes.Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList();
在Login中,foreach
循环中访问的 AuthenticationScheme
属性更改为 Name
:
@{ var loginProviders = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToList(); if (loginProviders.Count == 0) { <div> <p> There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a> for details on setting up this ASP.NET application to support logging in via external services. </p> </div> } else { <form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal"> <div> <p> @foreach (var provider in loginProviders) { <button type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button> } </p> </div> </form> } }
ManageLoginsViewModel 属性更改
ManageLoginsViewModel
对象用于ManageController.cs的 ManageLogins
操作。 在1.x 项目中,对象的 OtherLogins
属性返回类型为 IList<AuthenticationDescription>
。 此返回类型需要 Microsoft.AspNetCore.Http.Authentication
的导入:
using System.Collections.Generic; using Microsoft.AspNetCore.Http.Authentication; using Microsoft.AspNetCore.Identity; namespace AspNetCoreDotNetCore1App.Models.ManageViewModels { public class ManageLoginsViewModel { public IList<UserLoginInfo> CurrentLogins { get; set; } public IList<AuthenticationDescription> OtherLogins { get; set; } } }
在2.0 项目中,返回类型更改为 IList<AuthenticationScheme>
。 这一新的返回类型需要使用 Microsoft.AspNetCore.Authentication
导入替换 Microsoft.AspNetCore.Http.Authentication
导入。
using System.Collections.Generic; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Identity; namespace AspNetCoreDotNetCore2App.Models.ManageViewModels { public class ManageLoginsViewModel { public IList<UserLoginInfo> CurrentLogins { get; set; } public IList<AuthenticationScheme> OtherLogins { get; set; } } }
其他资源
有关详细信息,请参阅 GitHub 上的有关 Auth 2.0问题的讨论。