將驗證和 Identity 移轉至 ASP.NET Core 2.0
作者 :Scott Addie 和 ,Kung
ASP.NET Core 2.0 具有新的驗證模型,可 Identity 簡化使用服務的設定。 ASP.NET Core使用驗證或 Identity 更新的 1.x 應用程式,以使用新的模型,如下所述。
更新命名空間
在 1.x 中,在 命名空間中找到這類 IdentityRole
和 IdentityUser
類別 Microsoft.AspNetCore.Identity.EntityFrameworkCore
。
在 2.0 中 Microsoft.AspNetCore.Identity ,命名空間成為數個這類類別的新首頁。 使用預設 Identity 程式碼時,受影響的類別包括 ApplicationUser
和 Startup
。 調整語句 using
以解析受影響的參考。
驗證中介軟體和服務
在 1.x 專案中,驗證是透過中介軟體進行設定。 系統會針對您想要支援的每個驗證配置叫用中介軟體方法。
下列 1.x 範例會在 中 Startup.cs
設定 Identity 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
設定 Identity 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 s Identity
將 取代
UseIdentity
為UseAuthentication
方法中的Configure
:app.UseAuthentication();
AddIdentity
叫用 方法中的ConfigureServices
方法,以新增 cookie 驗證服務。或者,在 方法中
ConfigureServices
叫ConfigureApplicationCookie
用 或ConfigureExternalCookie
方法,以調整 Identitycookie 設定。services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn");
不使用 cookieIdentity
將
UseCookieAuthentication
方法中的Configure
方法呼叫取代為UseAuthentication
:app.UseAuthentication();
在
AddAuthentication
方法中ConfigureServices
叫用 和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
中進行下列變更:
將
UseJwtBearerAuthentication
方法中的Configure
方法呼叫取代為UseAuthentication
:app.UseAuthentication();
在
AddJwtBearer
方法中叫用ConfigureServices
方法:services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Audience = "http://localhost:5001/"; options.Authority = "http://localhost:5000/"; });
此程式碼片段不會使用 Identity ,因此預設配置應該藉由傳遞
JwtBearerDefaults.AuthenticationScheme
至AddAuthentication
方法來設定。
OpenID Connect (OIDC) 驗證
在 Startup.cs
中進行下列變更:
將
UseOpenIdConnectAuthentication
方法中的Configure
方法呼叫取代為UseAuthentication
:app.UseAuthentication();
在
AddOpenIdConnect
方法中叫用ConfigureServices
方法: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"]; });
將
PostLogoutRedirectUri
動作中的OpenIdConnectOptions
屬性取代為SignedOutRedirectUri
:.AddOpenIdConnect(options => { options.SignedOutRedirectUri = "https://contoso.com"; });
Facebook 驗證
在 Startup.cs
中進行下列變更:
將
UseFacebookAuthentication
方法中的Configure
方法呼叫取代為UseAuthentication
:app.UseAuthentication();
在
AddFacebook
方法中叫用ConfigureServices
方法:services.AddAuthentication() .AddFacebook(options => { options.AppId = Configuration["auth:facebook:appid"]; options.AppSecret = Configuration["auth:facebook:appsecret"]; });
Google 驗證
在 Startup.cs
中進行下列變更:
將
UseGoogleAuthentication
方法中的Configure
方法呼叫取代為UseAuthentication
:app.UseAuthentication();
在
AddGoogle
方法中叫用ConfigureServices
方法:services.AddAuthentication() .AddGoogle(options => { options.ClientId = Configuration["auth:google:clientid"]; options.ClientSecret = Configuration["auth:google:clientsecret"]; });
Microsoft 帳戶驗證
如需 Microsoft 帳戶驗證的詳細資訊,請參閱 此 GitHub 問題。
在 Startup.cs
中進行下列變更:
將
UseMicrosoftAccountAuthentication
方法中的Configure
方法呼叫取代為UseAuthentication
:app.UseAuthentication();
在
AddMicrosoftAccount
方法中叫用ConfigureServices
方法:services.AddAuthentication() .AddMicrosoftAccount(options => { options.ClientId = Configuration["auth:microsoft:clientid"]; options.ClientSecret = Configuration["auth:microsoft:clientsecret"]; });
Twitter 驗證
在 Startup.cs
中進行下列變更:
將
UseTwitterAuthentication
方法中的Configure
方法呼叫取代為UseAuthentication
:app.UseAuthentication();
在
AddTwitter
方法中叫用ConfigureServices
方法:services.AddAuthentication() .AddTwitter(options => { options.ConsumerKey = Configuration["auth:twitter:consumerkey"]; options.ConsumerSecret = Configuration["auth:twitter:consumersecret"]; });
設定預設驗證配置
在 1.x 中, AutomaticAuthenticate
基類的 AuthenticationOptions 和 AutomaticChallenge
屬性是要在單一驗證配置上設定。 沒有好方法可以強制執行此動作。
在 2.0 中,這兩個屬性已移除為個別 AuthenticationOptions
實例上的屬性。 它們可以在 AddAuthentication
的 方法 Startup.cs
呼叫中 ConfigureServices
設定:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
在上述程式碼片段中,預設配置會設定為 CookieAuthenticationDefaults.AuthenticationScheme
(「s」 Cookie ) 。
或者,使用 方法的多 AddAuthentication
載版本來設定多個屬性。 在下列多載方法範例中,預設配置會設定為 CookieAuthenticationDefaults.AuthenticationScheme
。 您也可以在個別 [Authorize]
屬性或授權原則內指定驗證配置。
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
});
如果下列其中一個條件成立,請在 2.0 中定義預設配置:
- 您希望使用者自動登入
- 您不需要指定配置,即可
[Authorize]
使用屬性或授權原則
這個規則的例外狀況是 AddIdentity
方法。 這個方法會為您新增 cookie ,並將預設的驗證和挑戰配置設定為應用程式 cookieIdentityConstants.ApplicationScheme
。 此外,它會將預設登入配置設定為外部 cookieIdentityConstants.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
設定預設配置。若為 Microsoft.AspNetCore.Server.IISIntegration,請將預設配置設定為
IISDefaults.AuthenticationScheme
:using Microsoft.AspNetCore.Server.IISIntegration; services.AddAuthentication(IISDefaults.AuthenticationScheme);
若為 Microsoft.AspNetCore.Server.HttpSys,請將預設配置設定為
HttpSysDefaults.AuthenticationScheme
:using Microsoft.AspNetCore.Server.HttpSys; services.AddAuthentication(HttpSysDefaults.AuthenticationScheme);
無法設定預設配置可防止授權 (挑戰) 要求使用下列例外狀況:
System.InvalidOperationException
:未指定 authenticationScheme,而且找不到 DefaultChallengeScheme。
如需詳細資訊,請參閱在 ASP.NET Core 中設定 Windows 驗證。
IdentityCookie選項實例
2.0 變更的副作用是切換為使用具名選項,而不是 cookie 選項實例。 移除自訂 Identitycookie 配置名稱的能力。
例如,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;
新增 Identity 使用者 POCO 流覽屬性
已移除基底 IdentityUser
POCO (純舊 CLR 物件) 的 Entity Framework (EF) Core 導覽屬性。 如果您的 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 防止重複的外鍵,請在呼叫) 之後 base.OnModelCreating();
,將下列內容新增至類別 IdentityDbContext
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 專案在 中 Controllers/ManageController.cs
具有下列程式碼:
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.cshtml
,迴圈中 foreach
存取的屬性會變更為 Name
AuthenticationScheme
:
@{
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
物件用於 ManageLogins
的 ManageController.cs
動作中。 在 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.Http.Authentication
Microsoft.AspNetCore.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 問題討論 。