ASP.NET Core 上的 Identity 简介
ASP.NET Core Identity:
一个 API,它支持用户界面 (UI) 登录功能。
管理用户、密码、配置文件数据、角色、声明、令牌、电子邮件确认等等。
用户可使用存储在 Identity 中的登录信息创建帐户,或者可使用外部登录提供程序。 支持的外部登录提供程序包括 Facebook、Google、Microsoft 帐户和 Twitter。
有关如何全局要求所有用户进行身份验证的信息,请参阅需要通过身份验证的用户。
可在 GitHub 上获取 " data-linktype="external">Identity 源代码。 Scaffold Identity 和查看生成的文件,以查看与 Identity 的模板交互。
Identity 通常使用 SQL Server 数据库进行配置,以存储用户名、密码和配置文件数据。 或者,可使用其他持久性存储,例如 Azure 表存储。
在本主题中,你将学习如何使用 Identity 来注册、登录和注销用户。 注意:模板会将用户的用户名和电子邮件看做是相同的。 若要更详细了解如何创建使用 Identity 的应用,请参阅Identity。
Azure Active Directory (Azure AD) 开发人员平台的演变。
与 ASP.NET Core Identity 无关。
ASP.NET Core Identity 将用户界面 (UI) 登录功能添加到 ASP.NET Core Web 应用。 若要保护 Web API 和 SPA,请使用以下项之一:
IdentityServer4 是适用于 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 框架。 IdentityServer4 支持以下安全功能:
身份验证即服务 (AaaS)
跨多个应用程序类型的单一登录/注销 (SSO)
API 的访问控制
Federation Gateway
有关详细信息,请参阅 Welcome to IdentityServer4。
创建使用身份验证的 Web 应用
使用个人用户帐户创建“ASP.NET Core Web 应用程序”项目。
选择“ASP.NET Core Web 应用”模板。 将项目命名为 WebApp1,使其命名空间与项目下载项相同。 单击“确定” 。
在“身份验证类型”输入中,选择“个人用户帐户”。
生成的项目提供 ASP.NET Core Identity 作为 Razor Class Library。 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”“查看数据”:
配置 Identity 服务
这些服务添加在 Program.cs 中。 典型模式是按以下顺序调用方法:
Add{Service}
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 来启用 Identity。 UseAuthentication
向请求管道添加身份验证UseAuthentication
。
模板生成的应用不使用授权。 app.UseAuthorization
会被包含来内,确保在应用添加授权时按正确的顺序添加它。 必须按上述代码中所示的顺序调用 UseRouting
、UseAuthentication
、UseAuthorization
和 UseEndpoints
。
有关 IdentityOptions
的详细信息,请参阅 IdentityOptions 和IdentityOptions
。
构建 Register、Login、LogOut 和 RegisterConfirmation 的基架
添加 Register
、Login
、LogOut
和 RegisterConfirmation
文件。 按照 Scaffold identity into a Razor project with authorization 说明生成本部分中所示的代码。
检查注册
当用户单击 页面上的“注册”按钮时,会调用 RegisterModel.OnPostAsync
操作。 用户由 _userManager
对象上的 _UserManager_1_CreateAsync__0_System_String_" data-linktype="absolute-path">CreateAsync 创建:
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
仅用于测试,应在生产应用中禁用自动帐户验证。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
操作。 对 _signInManager
对象调用 PasswordSignInAsync
。
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 中的授权简介。
注销
“注销”链接会调用 操作。
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();
需要是重定向,这样浏览器才会执行新请求,用户的标识才会更新。
_SignInManager_1_SignOutAsync" data-linktype="absolute-path">SignOutAsync 会清除 cookie 中存储的用户声明。
Post 是在 Pages/Shared/_LoginPartial.cshtml 中指定:
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
<ul>
@if (SignInManager.IsSignedIn(User))
{
<li>
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index"
title="Manage">Hello @User.Identity.Name!</a>
</li>
<li>
<form 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>
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
</li>
<li>
<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 包都包含在 Identity中。
Identity 的主包是 IdentityIdentity/" data-linktype="external">Microsoft.AspNetCore.Identity。 此包中有 ASP.NET Core Identity 的核心接口集,由 Microsoft.AspNetCore.Identity.EntityFrameworkCore
提供。
迁移到 ASP.NET Core Identity
有关迁移现有 Identity 存储的详细信息和指南,请参阅Migrate Authentication and Identity。
设置密码强度
有关设置最小密码要求的示例,请查看配置。
AddDefaultIdentity 和 AddIdentity
ASP.NET Core 2.1 中引入了 AddDefaultIdentity。 调用 AddDefaultIdentity
类似于调用以下内容:
有关详细信息,请参阅 /UI/src/IdentityServiceCollectionUIExtensions.cs#L47-L63" data-linktype="external">AddDefaultIdentity 源。
禁止发布静态 Identity 资产
若要防止将静态 Identity 资产(Identity UI 的样式表和 JavaScript 文件)发布到 Web 根,请将以下 ResolveStaticWebAssetsInputsDependsOn
属性和 RemoveIdentityAssets
目标添加到应用的项目文件中:
<PropertyGroup>
<ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>
<Target Name="RemoveIdentityAssets">
<ItemGroup>
<StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
</ItemGroup>
</Target>
后续步骤
若要了解如何使用 SQLite 配置 ,请查看此 GitHub 问题。
Add, download, and delete user data to Identity in an ASP.NET Core project
ASP.NET Core Identity:
一个 API,它支持用户界面 (UI) 登录功能。
管理用户、密码、配置文件数据、角色、声明、令牌、电子邮件确认等等。
用户可使用存储在 Identity 中的登录信息创建帐户,或者可使用外部登录提供程序。 支持的外部登录提供程序包括 Facebook、Google、Microsoft 帐户和 Twitter。
有关如何全局要求所有用户进行身份验证的信息,请参阅需要通过身份验证的用户。
可在 GitHub 上获取 " data-linktype="external">Identity 源代码。 Scaffold Identity 和查看生成的文件,以查看与 Identity 的模板交互。
Identity 通常使用 SQL Server 数据库进行配置,以存储用户名、密码和配置文件数据。 或者,可使用其他持久性存储,例如 Azure 表存储。
在本主题中,你将学习如何使用 Identity 来注册、登录和注销用户。 注意:模板会将用户的用户名和电子邮件看做是相同的。 若要更详细了解如何创建使用 Identity 的应用,请参阅Identity。
Azure Active Directory (Azure AD) 开发人员平台的演变。
与 ASP.NET Core Identity 无关。
ASP.NET Core Identity 将用户界面 (UI) 登录功能添加到 ASP.NET Core Web 应用。 若要保护 Web API 和 SPA,请使用以下项之一:
IdentityServer4 是适用于 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 框架。 IdentityServer4 支持以下安全功能:
身份验证即服务 (AaaS)
跨多个应用程序类型的单一登录/注销 (SSO)
API 的访问控制
Federation Gateway
有关详细信息,请参阅 Welcome to IdentityServer4。
创建使用身份验证的 Web 应用
使用个人用户帐户创建“ASP.NET Core Web 应用程序”项目。
选择“文件”“新建”“项目”。
选择“ASP.NET Core Web 应用程序”。 将项目命名为 WebApp1,使其命名空间与项目下载项相同。 单击“确定” 。
选择 ASP.NET Core Web 应用程序,然后选择“更改身份验证”。
选择“个人用户帐户”,然后单击“确定”。
生成的项目提供 ASP.NET Core Identity 作为 Razor Class Library。 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”“查看数据”:
配置 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。 可通过依赖关系注入向应用提供服务。
通过调用 UseAuthentication 启用 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 来启用 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
会被包含来内,确保在应用添加授权时按正确的顺序添加它。 必须按上述代码中所示的顺序调用 UseRouting
、UseAuthentication
、UseAuthorization
和 UseEndpoints
。
有关 IdentityOptions
和 Startup
的详细信息,请参阅 IdentityOptions 和IdentityOptions
。
构建 Register、Login、LogOut 和 RegisterConfirmation 的基架
添加 Register
、Login
、LogOut
和 RegisterConfirmation
文件。 按照 Scaffold identity into a Razor project with authorization 说明生成本部分中所示的代码。
检查注册
当用户单击 页面上的“注册”按钮时,会调用 RegisterModel.OnPostAsync
操作。 用户由 _userManager
对象上的 _UserManager_1_CreateAsync__0_System_String_" data-linktype="absolute-path">CreateAsync 创建:
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
仅用于测试,应在生产应用中禁用自动帐户验证。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
操作。 对 _signInManager
对象调用 PasswordSignInAsync
。
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 中的授权简介。
注销
“注销”链接会调用 操作。
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();
需要是重定向,这样浏览器才会执行新请求,用户的标识才会更新。
_SignInManager_1_SignOutAsync" data-linktype="absolute-path">SignOutAsync 会清除 cookie 中存储的用户声明。
Post 是在 Pages/Shared/_LoginPartial.cshtml 中指定:
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
<ul>
@if (SignInManager.IsSignedIn(User))
{
<li>
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index"
title="Manage">Hello @User.Identity.Name!</a>
</li>
<li>
<form 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>
<a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
</li>
<li>
<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 包都包含在 Identity中。
Identity 的主包是 IdentityIdentity/" data-linktype="external">Microsoft.AspNetCore.Identity。 此包中有 ASP.NET Core Identity 的核心接口集,由 Microsoft.AspNetCore.Identity.EntityFrameworkCore
提供。
迁移到 ASP.NET Core Identity
有关迁移现有 Identity 存储的详细信息和指南,请参阅Migrate Authentication and Identity。
设置密码强度
有关设置最小密码要求的示例,请查看配置。
AddDefaultIdentity 和 AddIdentity
ASP.NET Core 2.1 中引入了 AddDefaultIdentity。 调用 AddDefaultIdentity
类似于调用以下内容:
有关详细信息,请参阅 /UI/src/IdentityServiceCollectionUIExtensions.cs#L47-L63" data-linktype="external">AddDefaultIdentity 源。
禁止发布静态 Identity 资产
若要防止将静态 Identity 资产(Identity UI 的样式表和 JavaScript 文件)发布到 Web 根,请将以下 ResolveStaticWebAssetsInputsDependsOn
属性和 RemoveIdentityAssets
目标添加到应用的项目文件中:
<PropertyGroup>
<ResolveStaticWebAssetsInputsDependsOn>RemoveIdentityAssets</ResolveStaticWebAssetsInputsDependsOn>
</PropertyGroup>
<Target Name="RemoveIdentityAssets">
<ItemGroup>
<StaticWebAsset Remove="@(StaticWebAsset)" Condition="%(SourceId) == 'Microsoft.AspNetCore.Identity.UI'" />
</ItemGroup>
</Target>
后续步骤
若要了解如何使用 SQLite 配置 ,请查看此 GitHub 问题。
Add, download, and delete user data to Identity in an ASP.NET Core project