Identity on ASP.NET Core 簡介

作者:Rick Anderson

IdentityASP.NET Core :

  • 這是支援使用者介面 (UI) 登入功能的 API。
  • 管理使用者、密碼、設定檔資料、角色、宣告、權杖、電子郵件確認等等。

使用者可以使用儲存在 中的 Identity 登入資訊來建立帳戶,也可以使用外部登入提供者。 支援的外部登入提供者包括Facebook、Google、Microsoft 帳戶和 Twitter

如需如何全域要求所有使用者通過驗證的資訊,請參閱 要求已驗證的使用者

原始程式碼 Identity可在 GitHub 上取得。 支架 Identity並檢視產生的檔案,以檢閱與 的 Identity 範本互動。

Identity通常會使用SQL Server資料庫來設定,以儲存使用者名稱、密碼和設定檔資料。 或者,也可以使用另一個永續性存放區,例如 Azure 資料表儲存體。

在本主題中,您將瞭解如何使用 Identity 來註冊、登入和登出使用者。 注意:範本會將使用者名稱和電子郵件視為相同的使用者。 如需有關建立使用 Identity 之應用程式的詳細指示,請參閱 後續步驟

IdentityASP.NET Core與Microsoft 身分識別平臺無關。 Microsoft 身分識別平臺為:

  • Azure Active Directory (Azure AD) 開發人員平臺的演進。
  • ASP.NET Core應用程式中驗證和授權的替代身分識別解決方案。

IdentityASP.NET Core會將使用者介面 (UI) 登入功能新增至 ASP.NET Core Web 應用程式。 若要保護 Web API 和 SPA,請使用下列其中一項:

IdentityServer4 是適用于 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 架構。 IdentityServer4 啟用下列安全性功能:

  • 驗證即服務 (AaaS)
  • 透過多個應用程式類型的單一登入/關閉 (SSO)
  • API 的存取控制
  • 同盟閘道

如需詳細資訊,請參閱 歡迎使用 Identity Server4

檢視或下載範例程式碼 , (如何下載) 。

使用驗證建立 Web 應用程式

使用個別使用者帳戶建立 ASP.NET Core Web 應用程式專案。

  • 選取 [ASP.NET Core Web 應用程式] 範本。 將專案命名為 WebApp1 ,其命名空間與專案下載相同。 按一下 [確定]。
  • 在 [驗證類型] 輸入中,選取 [個別使用者帳戶]。

產生的專案會以類別庫的形式 Razor提供 ASP.NET Core Identity。 類別 IdentityRazor 庫會公開 Identity 具有區域的端點。 例如:

  • / Identity /Account/Login
  • / Identity /Account/Logout
  • / Identity /Account/Manage

套用移轉

套用移轉以初始化資料庫。

在套件管理員主控台中執行下列命令, (PMC) :

Update-Database

測試註冊和登入

執行應用程式並註冊使用者。 視螢幕大小而定,您可能需要選取流覽切換按鈕,以查看 [註冊 ] 和 [ 登入] 連結。

檢視 Identity 資料庫

  • 從 [檢視] 功能表中,選取[SQL Server 物件總管 ( SSOX) ]。
  • 流覽至 (localdb) MSSQLLocalDB (SQL Server 13) 。 以滑鼠右鍵按一下dbo。AspNetUsers>檢視資料

SQL Server 物件總管 AspNetUsers 資料表上的操作功能表

設定 Identity 服務

服務會在 中 Program.cs 新增。 典型的模式是以下列順序呼叫方法:

  1. Add{Service}
  2. builder.Services.Configure{Service}
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebApp1.Data;

var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();

builder.Services.Configure<IdentityOptions>(options =>
{
    // Password settings.
    options.Password.RequireDigit = true;
    options.Password.RequireLowercase = true;
    options.Password.RequireNonAlphanumeric = true;
    options.Password.RequireUppercase = true;
    options.Password.RequiredLength = 6;
    options.Password.RequiredUniqueChars = 1;

    // Lockout settings.
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.Lockout.AllowedForNewUsers = true;

    // User settings.
    options.User.AllowedUserNameCharacters =
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
    options.User.RequireUniqueEmail = false;
});

builder.Services.ConfigureApplicationCookie(options =>
{
    // Cookie settings
    options.Cookie.HttpOnly = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

    options.LoginPath = "/Identity/Account/Login";
    options.AccessDeniedPath = "/Identity/Account/AccessDenied";
    options.SlidingExpiration = true;
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

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

app.UseRouting();

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

app.MapRazorPages();

app.Run();

上述程式碼會 Identity 使用預設選項值進行設定。 服務可透過 相依性插入提供給應用程式。

Identity 會呼叫 UseAuthentication 來啟用 。 UseAuthentication 將驗證 中介軟體 新增至要求管線。

範本產生的應用程式不會使用 授權app.UseAuthorization 包含在內,以確保應用程式新增授權的順序正確。 UseRoutingUseAuthenticationUseAuthorization 必須依上述程式碼所示的順序呼叫。

如需 的詳細資訊 IdentityOptions ,請參閱 IdentityOptions應用程式啟動

Scaffold Register、Login、LogOut 和 RegisterConfirmation

Register新增 、 LoginLogOutRegisterConfirmation 檔案。 依照 Scaffold 身分識別進入 Razor 具有授權指示的專案 ,以產生本節中顯示的程式碼。

檢查暫存器

當使用者按一下頁面上的 [ 註冊 ] 按鈕 Register 時, RegisterModel.OnPostAsync 會叫用動作。 使用者是由 CreateAsync(TUser) 在 物件上 _userManager 建立:

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

停用預設帳戶驗證

使用預設範本時,系統會將使用者重新導向至 Account.RegisterConfirmation 可選取連結以確認帳戶的連結。 預設值 Account.RegisterConfirmation僅用於 測試,生產應用程式中應該停用自動帳戶驗證。

若要要求已確認的帳戶,並防止在註冊時立即登入,請在 中 /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs 設定 DisplayConfirmAccountLink = false

[AllowAnonymous]
public class RegisterConfirmationModel : PageModel
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IEmailSender _sender;

    public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
    {
        _userManager = userManager;
        _sender = sender;
    }

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

    public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
    {
        if (email == null)
        {
            return RedirectToPage("/Index");
        }

        var user = await _userManager.FindByEmailAsync(email);
        if (user == null)
        {
            return NotFound($"Unable to load user with email '{email}'.");
        }

        Email = email;
        // Once you add a real email sender, you should remove this code that lets you confirm the account
        DisplayConfirmAccountLink = false;
        if (DisplayConfirmAccountLink)
        {
            var userId = await _userManager.GetUserIdAsync(user);
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            EmailConfirmationUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                protocol: Request.Scheme);
        }

        return Page();
    }
}

登入

登入表單會在下列情況下顯示:

  • 已選取 [ 登入 ] 連結。
  • 使用者嘗試存取未獲授權存取的受限制頁面 ,或 系統尚未通過驗證時。

提交登入頁面上的表單時,會 OnPostAsync 呼叫動作。 PasswordSignInAsync 在 物件上 _signInManager 呼叫 。

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

如需如何做出授權決策的資訊,請參閱ASP.NET Core中的授權簡介

登出

登出連結會叫用 LogoutModel.OnPost 動作。

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

在上述程式碼中,程式碼 return RedirectToPage(); 必須是重新導向,讓瀏覽器執行新的要求,並更新使用者的身分識別。

SignOutAsync 清除儲存在 中的 cookie 使用者宣告。

Post 是在 中 Pages/Shared/_LoginPartial.cshtml 指定:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

測試 Identity

預設 Web 專案範本允許匿名存取首頁。 若要測試 Identity ,請新增 [Authorize]

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

如果您已登入,請登出。執行應用程式並選取 Privacy 連結。 系統會將您重新導向至 [登入] 頁面。

探討 Identity

若要更詳細地探索 Identity :

Identity 元件

Identity所有相依的 NuGet 套件都包含在ASP.NET Core共用架構中。

的主要套件 IdentityMicrosoft。AspNetCore. Identity. 此套件包含 ASP.NET Core Identity 的核心介面集,並由 包含 Microsoft.AspNetCore.Identity.EntityFrameworkCore

移轉至 ASP.NET Core Identity

如需移轉現有 Identity 存放區的詳細資訊和指引,請參閱移轉驗證和 Identity

設定密碼強度

如需設定最低密碼需求的範例,請參閱設定。

AddDefault Identity 和 AddIdentity

AddDefaultIdentity已在 ASP.NET Core 2.1 中引進。 呼叫 AddDefaultIdentity 類似于呼叫下列專案:

如需詳細資訊,請參閱 AddDefault Identity 來源

防止發佈靜態 Identity 資產

若要防止將靜態 Identity 資產 (樣式表單和 JavaScript 檔案 Identity 發佈至 Web 根目錄) ,請將下列 ResolveStaticWebAssetsInputsDependsOn 屬性和 RemoveIdentityAssets 目標新增至應用程式的專案檔:

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

後續步驟

作者:Rick Anderson

IdentityASP.NET Core:

  • 這是支援使用者介面的 API (UI) 登入功能。
  • 管理使用者、密碼、設定檔資料、角色、宣告、權杖、電子郵件確認等等。

使用者可以使用儲存在 中的 Identity 登入資訊來建立帳戶,也可以使用外部登入提供者。 支援的外部登入提供者包括Facebook、Google、Microsoft 帳戶和 Twitter

如需如何全域要求所有使用者進行驗證的資訊,請參閱 要求已驗證的使用者

Identity 原始程式碼可在 GitHub 上使用。 支架 Identity並檢視產生的檔案,以檢閱與 Identity 的範本互動。

Identity通常會使用SQL Server資料庫來設定,以儲存使用者名稱、密碼和設定檔資料。 或者,也可以使用另一個永續性存放區,例如 Azure 資料表儲存體。

在本主題中,您將瞭解如何使用 Identity 來註冊、登入和登出使用者。 注意:範本會將使用者名稱和電子郵件視為相同的使用者。 如需有關建立使用 Identity 之應用程式的詳細指示,請參閱 後續步驟

Microsoft 身分識別平臺為:

  • Azure Active Directory (Azure AD) 開發人員平臺的演進。
  • ASP.NET Core應用程式中驗證和授權的替代身分識別解決方案。
  • 與 ASP.NET Core Identity 無關。

IdentityASP.NET Core會將使用者介面 (UI) 登入功能新增至 ASP.NET Core Web 應用程式。 若要保護 Web API 和 SPA 的安全,請使用下列其中一項:

IdentityServer4 是適用于 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 架構。 IdentityServer4 啟用下列安全性功能:

  • 驗證即服務 (AaaS)
  • 單一登入/關閉 (SSO) 多個應用程式類型
  • API 的存取控制
  • 同盟閘道

如需詳細資訊,請參閱 歡迎使用 Identity Server4

檢視或下載範例程式碼 (如何下載) 。

使用驗證建立 Web 應用程式

使用個別使用者帳戶建立 ASP.NET Core Web 應用程式專案。

  • 選取 [File] \(檔案\)> [New] \(新增\)> [Project] \(專案\)。
  • 選取 ASP.NET Core Web 應用程式。 將專案命名為 WebApp1 ,其命名空間與專案下載相同。 按一下 [確定]。
  • 選取 ASP.NET Core Web 應用程式,然後選取[變更驗證]。
  • 選取 [個別使用者帳戶 ],然後按一下 [ 確定]。

產生的專案會以類別庫的形式提供ASP.NET Core IdentityRazor。 類別 IdentityRazor 庫會公開具有 Identity 區域的端點。 例如:

  • / Identity /Account/Login
  • / Identity /Account/Logout
  • / Identity /Account/Manage

套用移轉

套用移轉以初始化資料庫。

在套件管理員主控台中執行下列命令, (PMC) :

PM> Update-Database

測試註冊和登入

執行應用程式並註冊使用者。 視螢幕大小而定,您可能需要選取流覽切換按鈕,以查看 [註冊 ] 和 [ 登入] 連結。

檢視 Identity 資料庫

  • 從 [檢視] 功能表中,選取[SQL Server 物件總管 ( SSOX) ]。
  • 流覽至 (localdb) MSSQLLocalDB (SQL Server 13) 。 以滑鼠右鍵按一下dbo。AspNetUsers>檢視資料

SQL Server 物件總管 AspNetUsers 資料表上的操作功能表

設定 Identity 服務

服務會在 中 ConfigureServices 新增。 典型模式是呼叫所有 Add{Service} 方法,然後呼叫 services.Configure{Service} 方法。

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

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

上述醒目提示的程式碼會 Identity 使用預設選項值進行設定。 服務可透過 相依性插入提供給應用程式。

Identity 會呼叫 UseAuthentication 來啟用 。 UseAuthentication 將驗證 中介軟體 新增至要求管線。

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

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

    app.UseRouting();

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

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        // options.UseSqlite(
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));
    services.AddDatabaseDeveloperPageExceptionFilter();
    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();
    services.AddRazorPages();

    services.Configure<IdentityOptions>(options =>
    {
        // Password settings.
        options.Password.RequireDigit = true;
        options.Password.RequireLowercase = true;
        options.Password.RequireNonAlphanumeric = true;
        options.Password.RequireUppercase = true;
        options.Password.RequiredLength = 6;
        options.Password.RequiredUniqueChars = 1;

        // Lockout settings.
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.AllowedForNewUsers = true;

        // User settings.
        options.User.AllowedUserNameCharacters =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
        options.User.RequireUniqueEmail = false;
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings
        options.Cookie.HttpOnly = true;
        options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

        options.LoginPath = "/Identity/Account/Login";
        options.AccessDeniedPath = "/Identity/Account/AccessDenied";
        options.SlidingExpiration = true;
    });
}

上述程式碼會 Identity 使用預設選項值進行設定。 服務可透過 相依性插入提供給應用程式。

Identity 會呼叫 UseAuthentication 來啟用 。 UseAuthentication 將驗證 中介軟體 新增至要求管線。

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

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

    app.UseRouting();

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

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

範本產生的應用程式不會使用 授權app.UseAuthorization 包含在內,以確保應用程式新增授權的順序正確。 UseRoutingUseAuthenticationUseAuthorizationUseEndpoints 必須依上述程式碼所示的順序呼叫。

如需 和 Startup 的詳細資訊 IdentityOptions ,請參閱 IdentityOptions應用程式啟動

Scaffold Register、Login、LogOut 和 RegisterConfirmation

Register新增 、 LoginLogOutRegisterConfirmation 檔案。 依照 Scaffold 身分識別進入 Razor 具有授權指示的專案 ,以產生本節中顯示的程式碼。

檢查暫存器

當使用者按一下頁面上的 [ 註冊 ] 按鈕 Register 時, RegisterModel.OnPostAsync 會叫用動作。 使用者是由 CreateAsync(TUser) 在 物件上 _userManager 建立:

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");
    ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
                                          .ToList();
    if (ModelState.IsValid)
    {
        var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (result.Succeeded)
        {
            _logger.LogInformation("User created a new account with password.");

            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = user.Id, code = code },
                protocol: Request.Scheme);

            await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

            if (_userManager.Options.SignIn.RequireConfirmedAccount)
            {
                return RedirectToPage("RegisterConfirmation", 
                                      new { email = Input.Email });
            }
            else
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                return LocalRedirect(returnUrl);
            }
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

停用預設帳戶驗證

使用預設範本時,系統會將使用者重新導向至 Account.RegisterConfirmation 可選取連結以確認帳戶的連結。 預設值 Account.RegisterConfirmation僅用於 測試,生產應用程式中應該停用自動帳戶驗證。

若要要求已確認的帳戶,並防止在註冊時立即登入,請在 中 /Areas/Identity/Pages/Account/RegisterConfirmation.cshtml.cs 設定 DisplayConfirmAccountLink = false

[AllowAnonymous]
public class RegisterConfirmationModel : PageModel
{
    private readonly UserManager<IdentityUser> _userManager;
    private readonly IEmailSender _sender;

    public RegisterConfirmationModel(UserManager<IdentityUser> userManager, IEmailSender sender)
    {
        _userManager = userManager;
        _sender = sender;
    }

    public string Email { get; set; }

    public bool DisplayConfirmAccountLink { get; set; }

    public string EmailConfirmationUrl { get; set; }

    public async Task<IActionResult> OnGetAsync(string email, string returnUrl = null)
    {
        if (email == null)
        {
            return RedirectToPage("/Index");
        }

        var user = await _userManager.FindByEmailAsync(email);
        if (user == null)
        {
            return NotFound($"Unable to load user with email '{email}'.");
        }

        Email = email;
        // Once you add a real email sender, you should remove this code that lets you confirm the account
        DisplayConfirmAccountLink = false;
        if (DisplayConfirmAccountLink)
        {
            var userId = await _userManager.GetUserIdAsync(user);
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            EmailConfirmationUrl = Url.Page(
                "/Account/ConfirmEmail",
                pageHandler: null,
                values: new { area = "Identity", userId = userId, code = code, returnUrl = returnUrl },
                protocol: Request.Scheme);
        }

        return Page();
    }
}

登入

登入表單會在下列情況下顯示:

  • 已選取 [ 登入] 連結。
  • 使用者嘗試存取未獲授權的受限制頁面, 或在 系統尚未通過驗證時存取。

提交登入頁面上的表單時,會 OnPostAsync 呼叫動作。 PasswordSignInAsync_signInManager 物件上呼叫 。

public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    if (ModelState.IsValid)
    {
        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, 
        // set lockoutOnFailure: true
        var result = await _signInManager.PasswordSignInAsync(Input.Email,
                           Input.Password, Input.RememberMe, lockoutOnFailure: true);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        if (result.RequiresTwoFactor)
        {
            return RedirectToPage("./LoginWith2fa", new
            {
                ReturnUrl = returnUrl,
                RememberMe = Input.RememberMe
            });
        }
        if (result.IsLockedOut)
        {
            _logger.LogWarning("User account locked out.");
            return RedirectToPage("./Lockout");
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

如需如何做出授權決策的資訊,請參閱ASP.NET Core 中的授權簡介

登出

登出連結會叫用 LogoutModel.OnPost 動作。

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace WebApp1.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LogoutModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LogoutModel> _logger;

        public LogoutModel(SignInManager<IdentityUser> signInManager, ILogger<LogoutModel> logger)
        {
            _signInManager = signInManager;
            _logger = logger;
        }

        public void OnGet()
        {
        }

        public async Task<IActionResult> OnPost(string returnUrl = null)
        {
            await _signInManager.SignOutAsync();
            _logger.LogInformation("User logged out.");
            if (returnUrl != null)
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                return RedirectToPage();
            }
        }
    }
}

在上述程式碼中,程式碼 return RedirectToPage(); 必須是重新導向,讓瀏覽器執行新的要求,並更新使用者的身分識別。

SignOutAsync 清除儲存在 中的 cookie 使用者宣告。

Post 是在 中 Pages/Shared/_LoginPartial.cshtml 指定:

@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

<ul class="navbar-nav">
@if (SignInManager.IsSignedIn(User))
{
    <li class="nav-item">
        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" 
                                              title="Manage">Hello @User.Identity.Name!</a>
    </li>
    <li class="nav-item">
        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" 
                                  asp-route-returnUrl="@Url.Page("/", new { area = "" })" 
                                  method="post" >
            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
    </li>
}
</ul>

測試 Identity

預設 Web 專案範本允許匿名存取首頁。 若要測試 Identity ,請新增 [Authorize]

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace WebApp1.Pages
{
    [Authorize]
    public class PrivacyModel : PageModel
    {
        private readonly ILogger<PrivacyModel> _logger;

        public PrivacyModel(ILogger<PrivacyModel> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
        }
    }
}

如果您已登入,請登出。執行應用程式並選取 Privacy 連結。 系統會將您重新導向至 [登入] 頁面。

探討 Identity

若要更詳細地探索 Identity :

Identity 元件

Identity所有相依 NuGet 套件都包含在ASP.NET Core共用架構中。

的主要套件 Identity 為Microsoft。AspNetCore. Identity. 此套件包含 ASP.NET Core Identity 的核心介面集,且 隨附于 Microsoft.AspNetCore.Identity.EntityFrameworkCore

移轉至 ASP.NET Core Identity

如需移轉現有 Identity 存放區的詳細資訊和指引,請參閱移轉驗證和 Identity

設定密碼強度

如需設定最低密碼需求的範例,請參閱設定。

AddDefault Identity 和 AddIdentity

AddDefaultIdentity已在 ASP.NET Core 2.1 中引進。 呼叫 AddDefaultIdentity 類似于呼叫下列內容:

如需詳細資訊,請參閱 AddDefault Identity 來源

防止發佈靜態 Identity 資產

若要防止將靜態 Identity 資產發佈 (樣式表單和適用于 UI 的 JavaScript 檔案 Identity) 至 Web 根目錄,請將下列 ResolveStaticWebAssetsInputsDependsOn 屬性和 RemoveIdentityAssets 目標新增至應用程式的專案檔:

<PropertyGroup>
  <ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>

<Target Name="RemoveIdentityAssets">
  <ItemGroup>
    <StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
  </ItemGroup>
</Target>

後續步驟