如何使用 Identity 保护 SPA 的 Web API 后端
注意
此版本不是本文的最新版本。 有关当前版本的信息,请参阅本文的.NET 9版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 有关当前版本,请参阅.NET 9 版本的本文。
ASP.NET Core Identity 提供用于处理身份验证、授权和标识管理的 API。 通过 API,可以使用基于 cookie 身份验证保护 Web API 后端的终结点。 基于令牌的选项适用于无法使用 Cookie 的客户端,但在使用此选项时,你负责确保令牌保持安全。 建议对基于浏览器的应用程序使用 Cookie,因为默认情况下,浏览器会自动处理它们,而不会将它们公开给 JavaScript。
本文演示如何使用 Identity 保护 SPA 的 Web API 后端,例如 Angular、React 和 Vue 应用。 同一后端 API 可用于保护 Blazor WebAssembly 应用。
先决条件
本文中介绍的步骤将身份验证和授权添加到 ASP.NET 核心 Web API 应用,该应用:
- 尚未为身份验证配置。
- 以
net8.0
或更高版本为目标。 - 可以是最小 API 或基于控制器的 API。
本文中的一些测试说明使用了项目模板随附的 Swagger UI。 Swagger UI 不需要与 Web API 后端一起使用 Identity。
安装 NuGet 包
安装以下 NuGet 包:
Microsoft.AspNetCore.Identity.EntityFrameworkCore
- 允许 Identity 使用 Entity Framework Core(EF Core)。- 使 EF Core 能够处理数据库,例如以下包之一:
若要快速开始使用,请使用内存中数据库。
稍后将数据库更改为 SQLite 或 SQL Server,以便在测试或供生产使用时在会话之间保存用户数据。 与内存中相比,这引入了一些复杂性,因为它需要通过迁移创建数据库,如入门教程EF Core所示。
使用 Visual Studio 中的 NuGet 包管理器或 dotnet add 包 CLI 命令安装这些包。
创建 IdentityDbContext
添加一个从 ApplicationDbContext
继承的名为 IdentityDbContext<TUser> 的类:
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) :
base(options)
{ }
}
显示的代码提供了一个特殊的构造函数,可用于为不同的环境配置数据库。
添加这些步骤中显示的代码时,根据需要添加以下 using
一个或多个指令。
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
配置 EF Core 下文
如前所述,最简单的入门方法是使用内存中数据库。 在内存中,每次运行都以新的数据库开头,无需使用迁移。 调用 WebApplication.CreateBuilder(args)
后,添加以下代码以配置 Identity 以使用内存数据库。
builder.Services.AddDbContext<ApplicationDbContext>(
options => options.UseInMemoryDatabase("AppDb"));
若要在测试或供生产使用时在会话之间保存用户数据,请稍后将数据库更改为 SQLite 或 SQL Server。
向容器添加 Identity 服务
调用 WebApplication.CreateBuilder(args)
,调用 AddAuthorization 以将服务添加到依赖项注入 (DI) 容器:
builder.Services.AddAuthorization();
激活 Identity API
调用 WebApplication.CreateBuilder(args)
之后,再调用 AddIdentityApiEndpoints<TUser>(IServiceCollection) 和 AddEntityFrameworkStores<TContext>(IdentityBuilder)。
builder.Services.AddIdentityApiEndpoints<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
默认情况下,Cookie 和专有令牌都是启用的。 如果登录终结点中的 useCookies
查询字符串参数为 true
,则会在登录时发出 Cookie 和令牌。
映射 Identity 路由
调用 builder.Build()
后,调用 MapIdentityApi<TUser>(IEndpointRouteBuilder) 以映射 Identity 终结点:
app.MapIdentityApi<IdentityUser>();
保护所选的终结点
若要保护终结点,请在定义路由的 RequireAuthorization 调用上使用 Map{Method}
扩展方法。 例如:
app.MapGet("/weatherforecast", (HttpContext httpContext) =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
})
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi()
.RequireAuthorization();
RequireAuthorization
方法还可用于:
保护 Swagger UI 终结点,如以下示例所示:
app.MapSwagger().RequireAuthorization();
使用特定声明或权限进行保护,如以下示例所示:
.RequireAuthorization("Admin");
在基于控制器的 Web API 项目中,通过将 [Authorize
] 属性应用于控制器或操作来保护终结点。
测试 API
测试身份验证的一种快速方法是使用项目模板附带的内存中数据库和 Swagger UI。 以下步骤演示如何使用 Swagger UI 测试 API。 确保 Swagger UI 端点不受保护。
尝试访问受保护的终结点
- 运行应用并导航到 Swagger UI。
- 展开受保护的终结点,例如 Web API 模板在项目中创建的
/weatherforecast
。 - 选择试用。
- 选择“执行”。 响应为
401 - not authorized
。
测试注册
展开
/register
并选择 试用。在 UI 的“参数”部分中,会显示示例请求正文:
{ "email": "string", "password": "string" }
将“string”替换为有效的电子邮件地址和密码,然后选择“执行”。
要遵守默认密码验证规则,密码长度必须至少为 6 个字符,并且至少包含以下每种字符之一:
- 大写字母
- 小写字母
- 数字
- 非字母数字字符
如果输入无效的电子邮件地址或密码错误,则结果包括验证错误。 下面是包含验证错误的响应正文的示例:
{ "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1", "title": "One or more validation errors occurred.", "status": 400, "errors": { "PasswordTooShort": [ "Passwords must be at least 6 characters." ], "PasswordRequiresNonAlphanumeric": [ "Passwords must have at least one non alphanumeric character." ], "PasswordRequiresDigit": [ "Passwords must have at least one digit ('0'-'9')." ], "PasswordRequiresLower": [ "Passwords must have at least one lowercase ('a'-'z')." ] } }
错误是以 ProblemDetails 格式返回的,因此客户端可以对其进行分析,并根据需要显示验证错误。
成功注册会带来
200 - OK
响应。
测试登录
展开
/login
并选择“试用”。示例请求正文显示两个附加参数:{ "email": "string", "password": "string", "twoFactorCode": "string", "twoFactorRecoveryCode": "string" }
此示例不需要额外的 JSON 属性,可以删除。 将
useCookies
设置为true
。将“string”替换为用于注册的电子邮件地址和密码,然后选择“执行”。
成功登录会返回一个带有
200 - OK
的响应,其中响应标头中有 cookie。
重新测试安全终结点
成功登录后,重新尝试访问受保护的端点。 cookie 身份验证随请求一起自动发送,并授权终结点。 基于 Cookie 的身份验证安全地内置到浏览器,并“正常工作”。
使用非浏览器客户端进行测试
默认情况下,某些 Web 客户端的标头中可能不包括 Cookie:
如果使用工具来测试 API,则可能需要在设置中启用 Cookie。
默认情况下,JavaScript
fetch
API 不包含 Cookie。 通过在选项中将credentials
设置为值include
,以启用它们。在 Blazor WebAssembly 应用中运行的
HttpClient
需要HttpRequestMessage
,以包括凭据,如下所示:request.SetBrowserRequestCredential(BrowserRequestCredentials.Include);
使用基于令牌的身份验证
建议在基于浏览器的应用程序中使用 Cookie,因为默认情况下,浏览器会自动处理它们,而不会将它们公开给 JavaScript。
发布了一个自定义令牌(ASP.NET Core 标识平台专有的令牌),可用于对后续请求进行身份验证。 该令牌在 Authorization
标头中作为持有者令牌传递。 此外还会提供刷新令牌。 此令牌允许应用在旧令牌过期时请求新令牌,而无需强制用户再次登录。
该令牌不是标准的 JSON Web 令牌 (JWT)。 使用自定义令牌是有意而为的,因为内置 Identity API 主要用于简单方案。 令牌选项并非旨在作为功能全面的身份服务提供商或令牌服务器,而是为不能使用 Cookie 的客户端提供一种 cookie 选项的替代方案。
要使用基于令牌的身份验证,请在调用 useCookies
终结点时将 false
查询字符串参数设置为 /login
。 令牌使用持有者身份验证方案。 使用从对 /login
的调用中返回的令牌,对受保护终结点的后续调用应添加标头 Authorization: Bearer <token>
,其中 <token>
为访问令牌。 有关详细信息,请参阅本文后面的使用 POST /login
终结点。
注销
要为用户提供注销方法,请定义一个 /logout
终结点,如以下示例所示:
app.MapPost("/logout", async (SignInManager<IdentityUser> signInManager,
[FromBody] object empty) =>
{
if (empty != null)
{
await signInManager.SignOutAsync();
return Results.Ok();
}
return Results.Unauthorized();
})
.WithOpenApi()
.RequireAuthorization();
调用此终结点时,在请求正文中提供空 JSON 对象 ({}
)。 以下代码是调用注销接口的示例:
public signOut() {
return this.http.post('/logout', {}, {
withCredentials: true,
observe: 'response',
responseType: 'text'
MapIdentityApi<TUser>
终结点
对 MapIdentityApi<TUser>
进行调用会向应用添加以下终结点:
POST /register
POST /login
POST /refresh
GET /confirmEmail
POST /resendConfirmationEmail
POST /forgotPassword
POST /resetPassword
POST /manage/2fa
GET /manage/info
POST /manage/info
使用 POST /register
终结点
{
"email": "string",
"password": "string",
}
有关详细信息,请参阅:
- 本文前面的测试注册。
- RegisterRequest。
使用 POST /login
终结点
在请求正文中,Email 和 Password 是必需的。 如果启用了双因素身份验证 (2FA),则 TwoFactorCode 或 TwoFactorRecoveryCode 是必需的。 如果未启用 2FA,则省略 twoFactorCode
和 twoFactorRecoveryCode
。 有关详细信息,请参阅本文后面的使用 POST /manage/2fa
终结点。
下面是未启用 2FA 的请求正文示例:
{
"email": "string",
"password": "string"
}
下面是启用了 2FA 的请求正文示例:
-
{ "email": "string", "password": "string", "twoFactorCode": "string", }
-
{ "email": "string", "password": "string", "twoFactorRecoveryCode": "string" }
终结点需要查询字符串参数:
useCookies
- 设置为true
,以进行基于 cookie 的身份验证。 设置为false
或将其省略,以进行基于令牌的身份验证。
有关基于 cookie 的身份验证的详细信息,请参阅本文前面的测试登录。
基于令牌的身份验证
如果 useCookies
为 false
或省略,则启用基于令牌的身份验证。 响应正文包含以下属性:
{
"tokenType": "string",
"accessToken": "string",
"expiresIn": 0,
"refreshToken": "string"
}
有关这些属性的详细信息,请参阅 AccessTokenResponse。
将访问令牌放在标头中以发出经过身份验证的请求,如以下示例所示
Authorization: Bearer {access token}
当访问令牌即将过期时,调用 /refresh 终结点。
使用 POST /refresh
终结点
仅用于基于令牌的身份验证。 获取新的访问令牌,而无需强制用户再次登录。 访问令牌快要过期时,请调用此终端。
请求正文仅包含 RefreshToken。 下面是请求正文示例:
{
"refreshToken": "string"
}
如果调用成功,响应正文为新的 AccessTokenResponse,如以下示例所示:
{
"tokenType": "string",
"accessToken": "string",
"expiresIn": 0,
"refreshToken": "string"
}
使用 GET /confirmEmail
终结点
如果设置 Identity 以确认电子邮件,则成功调用终结点 /register
会发送一封电子邮件,其中包含指向 /confirmEmail
终结点的链接。 该链接包含以下查询字符串参数:
userId
code
changedEmail
- 仅当用户在注册期间更改了电子邮件地址时才包含。
Identity 为确认电子邮件提供默认文本。 默认情况下,电子邮件主题为“确认电子邮件”,电子邮件正文的外观则如以下示例所示:
Please confirm your account by <a href='https://contoso.com/confirmEmail?userId={user ID}&code={generated code}&changedEmail={new email address}'>clicking here</a>.
如果将 RequireConfirmedEmail 属性设置为 true
,则在通过单击电子邮件中的链接确认电子邮件地址之前,用户无法登录。 /confirmEmail
终结点:
- 确认电子邮件地址,以支持用户登录。
- 在响应正文中会返回文本“感谢您确认您的电子邮件”。
要设置 Identity 以实现电子邮件确认,请在 Program.cs
中添加代码,以将 RequireConfirmedEmail
设置为 true
,并向 DI 容器添加可实现 IEmailSender 的类。 例如:
builder.Services.Configure<IdentityOptions>(options =>
{
options.SignIn.RequireConfirmedEmail = true;
});
builder.Services.AddTransient<IEmailSender, EmailSender>();
有关详细信息,请参阅 ASP.NET Core 中的帐户确认和密码恢复。
Identity 还为需要发送的其他电子邮件提供默认文本,例如 2FA 和密码重置。 若要自定义这些电子邮件,请提供 IEmailSender
接口的自定义实现。 在前面的示例中,EmailSender
是实现 IEmailSender
的类。 有关详细信息,包括实现 IEmailSender
的类的示例,请参阅 在 ASP.NET Core 中确认帐户和恢复密码。
使用 POST /resendConfirmationEmail
终结点
仅当地址对已注册用户有效时,才发送电子邮件。
请求正文仅包含 Email。 下面是请求正文示例:
{
"email": "string"
}
有关更多信息,请参阅本文前面提到的使用 GET /confirmEmail
端点。
使用 POST /forgotPassword
终结点
生成包含密码重置代码的电子邮件。 使用新密码将该代码发送到 /resetPassword
。
请求正文仅包含 Email。 下面是一个示例:
{
"email": "string"
}
有关如何启用 Identity 来发送电子邮件的信息,请参阅使用 GET /confirmEmail
终结点。
使用 POST /resetPassword
终结点
通过调用 /forgotPassword
终结点在获取重置代码后调用此终结点。
请求正文需要 Email、ResetCode 和 NewPassword。 下面是一个示例:
{
"email": "string",
"resetCode": "string",
"newPassword": "string"
}
使用 POST /manage/2fa
终结点
为用户配置双因素身份验证 (2FA)。 启用 2FA 后,除了电子邮件地址和密码之外,成功登录还需要使用验证器应用生成的代码。
启用 2FA
要为当前经过身份验证的用户启用 2FA,请:
调用
/manage/2fa
终结点,以在请求正文中发送一个空的 JSON 对象 ({}
)。响应正文会提供 SharedKey,以及一些此时不需要的其他属性。 共享密钥用于设置验证器应用。 响应正文示例:
{ "sharedKey": "string", "recoveryCodesLeft": 0, "recoveryCodes": null, "isTwoFactorEnabled": false, "isMachineRemembered": false }
使用共享密钥获取基于时间的一次性密码(TOTP,即时间性一次性密码)。 有关详细信息,请参阅在 ASP.NET Core 中为 TOTP 验证器应用启用 QR 码生成。
调用
/manage/2fa
终结点,以在请求正文中发送 TOTP 和"enable": true
。 例如:{ "enable": true, "twoFactorCode": "string" }
响应正文确认 IsTwoFactorEnabled 为真,并提供 RecoveryCodes。 恢复代码用于在验证器应用不可用时进行登录。 成功启用 2FA 后的响应正文示例:
{ "sharedKey": "string", "recoveryCodesLeft": 10, "recoveryCodes": [ "string", "string", "string", "string", "string", "string", "string", "string", "string", "string" ], "isTwoFactorEnabled": true, "isMachineRemembered": false }
使用 2FA 登录
调用 /login
终结点,以便在请求正文中发送电子邮件地址、密码和 TOTP。 例如:
{
"email": "string",
"password": "string",
"twoFactorCode": "string"
}
如果用户无权访问验证器应用,请使用启用 2FA 时提供的恢复代码之一调用 /login
终结点来进行登录。 请求正文如下方示例所示:
{
"email": "string",
"password": "string",
"twoFactorRecoveryCode": "string"
}
重置恢复代码
要获取新的恢复代码集合,请在将 ResetRecoveryCodes 设置为 true
的情况下调用此终结点。 下面是请求正文示例:
{
"resetRecoveryCodes": true
}
重置共享密钥
要获取新的随机共享密钥,请在将 ResetSharedKey 设置为 true
的情况下调用此终结点。 下面是请求正文示例:
{
"resetSharedKey": true
}
重置密钥会自动禁用已通过身份验证用户的双因素认证登录要求,直到稍后的请求重新启用该要求。
忘记计算机
要清除 cookie“记住我标记”(如果存在),请在将 ForgetMachine 设置为 true 的情况下调用此终结点。 下面是请求正文示例:
{
"forgetMachine": true
}
此终结点不会影响基于令牌的身份验证。
使用 GET /manage/info
终结点
获取已登录用户的电子邮件地址和电子邮件确认状态。 出于安全原因,已从此终结点中省略声明。 如果需要声明,请使用服务器端 API 为声明设置终结点。 或提供接受声明并响应用户是否拥有该声明的验证终结点,而不是共享所有用户的声明。
请求不需要任何参数。 响应正文包括属性 Email 和 IsEmailConfirmed,如以下示例所示:
{
"email": "string",
"isEmailConfirmed": true
}
使用 POST /manage/info
终结点
更新已登录用户的电子邮件地址和密码。 在请求正文中发送 NewEmail、NewPassword 和 OldPassword,如以下示例所示:
{
"newEmail": "string",
"newPassword": "string",
"oldPassword": "string"
}
下面是响应正文的示例:
{
"email": "string",
"isEmailConfirmed": false
}
另请参阅
有关更多信息,请参阅以下资源:
- 选择标识管理解决方案
- 适用于 .NET Web 应用的Identity管理解决方案
- ASP.NET Core 中的简单授权
- 在 ASP.NET Core 项目中向 Identity 添加和下载用户数据,以及从中删除用户数据
- 创建一个通过授权保护用户数据的 ASP.NET Core 应用
- ASP.NET Core 中的帐户确认和密码恢复
- 为 ASP.NET Core 中的 TOTP 验证器应用启用 QR 码生成
- SPA的示例 Web API 后端 .http 文件显示基于令牌的身份验证。 例如:
- 未设置
useCookies
- 使用授权标头传递令牌
- 显示刷新以扩展会话,而无需强制用户再次登录
- 未设置
- 示例 Angular 应用,该应用使用 Identity 来保护 Web API 后端
ASP.NET Core 模板使用 API 授权支持在单页应用 (SPA) 中提供身份验证。 用于验证和存储用户身份信息的 ASP.NET Core Identity 与用于实现 Open ID Connect 的 Duende Identity Server 结合使用。
重要
Duende Software 可能会要求你为 Duende Identity Server 的生产使用支付许可证费用。 有关详细信息,请参阅从 ASP.NET Core 5.0 迁移到 6.0。
一个身份验证参数已添加到 Angular 和 React 项目模板,该参数类似于 Web 应用(模型-视图-控制器)(MVC) 和 Web 应用 ( Pages) 项目模板中的身份验证参数。 允许的参数值为 None 和 Individual。 React.js 和 Redux 项目模板目前不支持身份验证参数。
使用 API 授权支持创建应用
用户身份验证和授权可以与 Angular 和 React SPA 结合使用。 打开命令行界面,然后运行以下命令:
Angular:
dotnet new angular -au Individual
React:
dotnet new react -au Individual
上面的命令创建一个 ASP.NET Core 应用,它具有包含 SPA 的 ClientApp 目录。
应用的 ASP.NET Core 组件的一般说明
以下部分介绍了在包括身份验证支持的情况下对项目添加的内容:
Program.cs
下面的代码示例依赖于 Microsoft.AspNetCore.ApiAuthorization.IdentityServer NuGet 包。 该示例使用 AddApiAuthorization 和 AddIdentityServerJwt 扩展方法配置 API 身份验证和授权。 使用带身份验证的 React 或 Angular SPA 项目模板的项目包含对此包的引用。
dotnet new angular -au Individual
生成以下 Program.cs
文件:
using Microsoft.AspNetCore.Authentication;
using Microsoft.EntityFrameworkCore;
using output_directory_name.Data;
using output_directory_name.Models;
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapRazorPages();
app.MapFallbackToFile("index.html");
app.Run();
上述代码配置如下:
包含默认 UI 的 Identity:
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlite(connectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>();
包含附加
AddApiAuthorization
帮助器方法的 IdentityServer,该方法在 IdentityServer 基础上配置一些默认的 ASP.NET Core 约定:builder.Services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
使用附加
AddIdentityServerJwt
帮助器方法进行身份验证,该方法配置应用程序以验证由 IdentityServer 生成的 JWT 令牌。builder.Services.AddAuthentication() .AddIdentityServerJwt();
负责验证请求凭据并在请求上下文中设置用户的身份验证中间件:
app.UseAuthentication();
公开 OpenID Connect 端点的 IdentityServer 中间件:
app.UseIdentityServer();
警告
本文介绍连接字符串的使用。 使用本地数据库时,用户无需进行身份验证,但在生产环境中,连接字符串有时包括进行身份验证的密码。 资源所有者密码凭据(ROPC)是在生产数据库中应避免的安全风险。 生产应用应使用可用的最安全的身份验证流。 有关部署到测试或生产环境的应用的身份验证的详细信息,请参阅 安全身份验证流。
Linux 上的 Azure 应用服务
对于 Linux 上的 Azure 应用服务部署,请显式指定颁发者:
builder.Services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
options.Authority = "{AUTHORITY}";
});
在上面的代码中,{AUTHORITY}
占位符是在进行 OpenID Connect 调用时要使用的 Authority。
示例:
options.Authority = "https://contoso-service.azurewebsites.net";
AddApiAuthorization
此辅助方法将 IdentityServer 配置为使用我们支持的配置。 IdentityServer 是一个功能强大且可扩展的框架,用于处理应用安全问题。 同时,这暴露出了最常见情况下不必要的复杂性。 因此,向您提供了一套约定和配置选项,这被认为是一个不错的起点。 一旦身份验证需要更改,仍可使用 IdentityServer 的完整功能自定义身份验证以满足你的需求。
AddIdentityServerJwt
此帮助程序方法将应用的策略方案配置为默认身份验证处理程序。 该策略配置为允许 Identity 处理路由到 Identity URL 空间“/Identity”中任何子路径的所有请求。 JwtBearerHandler
处理所有其他请求。 此外,此方法向 IdentityServer 注册一个默认范围为 <<ApplicationName>>API
的 <<ApplicationName>>API
API 资源,并将 JWT 持有者令牌中间件配置为验证 IdentityServer 为应用颁发的令牌。
WeatherForecastController
在文件中,请注意应用于类的 [Authorize]
属性,该属性指示用户需要基于默认策略进行授权才能访问资源。 默认授权策略恰好配置为使用默认身份验证方案,而该方案由 AddIdentityServerJwt
设置为上面提到的策略方案,从而使此类帮助程序方法配置的 JwtBearerHandler
成为对应用进行的请求的默认处理程序。
ApplicationDbContext
在文件中,请注意,在 DbContext
中使用了相同的 Identity,只不过它会扩展 ApiAuthorizationDbContext
(一个来自 IdentityDbContext
的派生程度更高的类)以包括 IdentityServer 的架构。
要获取对数据库架构的完全控制,请从其中一个可用的 IdentityDbContext
类继承,并通过对 Identity 方法调用 builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value)
来配置上下文以包括 OnModelCreating
架构。
OidcConfigurationController
在文件中,请注意为处理客户端需要使用的 OIDC 参数而预配的终结点。
appsettings.json
在项目根目录的 appsettings.json
文件中,有一个新的 IdentityServer
部分,描述已配置的客户端列表。 下例中存在一个客户端。 客户端名称对应于应用名称,并通过约定映射到 OAuth ClientId
参数。 配置文件指示正在配置的应用类型。 它在内部用于促进简化服务器配置过程的约定。 有多个配置文件可用(在应用程序配置文件部分中进行了说明)。
"IdentityServer": {
"Clients": {
"angularindividualpreview3final": {
"Profile": "IdentityServerSPA"
}
}
}
appsettings.Development.json
在项目根目录的 appsettings.Development.json
文件中,有一个 IdentityServer
部分,描述用于对令牌进行签名的密钥。 部署到生产时,密钥需要与应用一起进行预配和部署(在部署到生产部分中进行了说明)。
"IdentityServer": {
"Key": {
"Type": "Development"
}
}
Angular 应用的一般说明
Angular 模板中的身份验证和 API 授权支持位于其自己的 Angular 模块中(在 ClientApp/src/api-authorization 目录中)。 该模块由以下元素组成:
- 3 个组件:
login.component.ts
:处理应用的登录流。logout.component.ts
:处理应用的登出流程。-
login-menu.component.ts
:显示以下链接集之一的小组件:- 用户经过身份验证时的用户资料管理和注销链接。
- 用户未经过身份验证时的注册和登录链接。
- 一个路由防护
AuthorizeGuard
,可以添加到路由,要求用户先经过身份验证,然后才能访问路由。 - 一个 HTTP 拦截器
AuthorizeInterceptor
,在用户经过身份验证时,将访问令牌附加到发送给 API 的 HTTP 请求中。 - 服务
AuthorizeService
处理身份验证过程的较低级别详细信息,并向应用的其余部分公开有关经过身份验证的用户的信息以供使用。 - 一个 Angular 模块,定义与应用的身份验证部分关联的路由。 它会将登录菜单组件、拦截器、保障措施和服务接口公开,以便应用的其他部分能够使用。
React 应用的一般说明
React 模板中的身份验证和 API 授权支持位于 ClientApp/src/components/api-authorization 目录中。 它由以下元素组成:
- 4 个组件:
Login.js
:处理应用的登录流。Logout.js
:处理应用的注销流程。-
LoginMenu.js
:显示以下链接集之一的小组件:- 用户已经过身份验证时的用户配置文件管理和注销链接。
- 用户未经过身份验证时的注册和登录链接。
AuthorizeRoute.js
:一个路由组件,要求用户先经过身份验证,然后才能呈现Component
参数中指示的组件。
- 一个导出的类
authService
实例AuthorizeService
,用于处理身份验证过程的较低级别详细信息,并将有关经过身份验证的用户的信息公开给应用的其余部分以供使用。
现在,你已了解了解决方案的主要组件,可以更深入地了解应用的各个方案。
要求对新 API 进行授权
默认情况下,系统配置为可轻松地要求为新 API 进行授权。 为此,请创建新控制器,并将 [Authorize]
属性添加到控制器类或控制器中的任何操作。
自定义 API 身份验证处理程序
若要自定义 API 的 JWT 处理程序的配置,请配置其 JwtBearerOptions 实例:
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
...
});
API 的 JWT 处理程序会引发相应事件,通过这些事件可使用 JwtBearerEvents
控制身份验证过程。 为了为 API 授权提供支持,AddIdentityServerJwt
会注册其自己的事件处理程序。
若要自定义事件的处理,请根据需要使用其他逻辑来包装现有事件处理程序。 例如:
builder.Services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
var onTokenValidated = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await onTokenValidated(context);
...
}
});
在上面的代码中,OnTokenValidated
事件处理程序替换为自定义实现。 此实现:
- 调用 API 授权支持提供的原始实现。
- 运行自己的自定义逻辑。
保护客户端路由 (Angular)
保护客户端路由的方法是,在配置路由时,将授权保障添加到要运行的保障列表中。 作为示例,可以查看如何在主应用 Angular 模块中配置 fetch-data
路由:
RouterModule.forRoot([
// ...
{ path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] },
])
必须指出的是,保护路由不会保护实际终结点(这仍然需要向它应用 [Authorize]
属性),而是只会阻止用户在未经过身份验证时导航到给定客户端路由。
对 API 请求进行身份验证 (Angular)
通过使用应用定义的 HTTP 客户端侦听器自动对与应用一起托管的 API 的请求进行身份验证。
保护客户端路由 (React)
使用 AuthorizeRoute
组件(而不是普通 Route
组件)保护客户端路由。 例如,请注意如何在 fetch-data
组件中配置 App
路由:
<AuthorizeRoute path='/fetch-data' component={FetchData} />
保护路由:
- 不保护实际终结点(这仍然需要向它应用
[Authorize]
属性)。 - 仅阻止用户在未经过身份验证时导航到给定客户端路由。
对 API 请求进行身份验证 (React)
使用 React 对请求进行身份验证的方式是首先从 authService
导入 AuthorizeService
实例。 访问令牌从 authService
进行检索并附加到请求,如下所示。 在 React 组件中,此工作通常在 componentDidMount
生命周期方法中完成,或是作为某种用户交互的结果。
将 authService
导入组件
import authService from './api-authorization/AuthorizeService'
检索访问令牌并将其附加到响应中
async populateWeatherData() {
const token = await authService.getAccessToken();
const response = await fetch('api/SampleData/WeatherForecasts', {
headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
this.setState({ forecasts: data, loading: false });
}
部署到生产
若要将应用部署到生产,需要预配以下资源:
- 用于存储 Identity 用户帐户和 IdentityServer 授权的数据库。
- 用于对令牌进行签名的生产证书。
- 此证书没有特定要求;它可以是自签名证书,或是通过 CA 颁发机构预配的证书。
- 它可以通过标准工具(如 PowerShell 或 OpenSSL)来生成。
- 它可以安装到目标计算机上的证书存储中,也可以作为具有强密码 .pfx 文件进行部署。
示例:部署到非 Azure Web 托管提供者
在 Web 托管面板中,创建或加载证书。 然后在应用的 appsettings.json
文件中,修改 IdentityServer
部分以包含密钥详细信息。 例如:
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "WebHosting",
"StoreLocation": "CurrentUser",
"Name": "CN=MyApplication"
}
}
在上面的示例中:
StoreName
表示用于存储证书的证书库的名称。 在此例中,它指向网站托管商店。StoreLocation
表示从中加载证书的位置(在此例中为CurrentUser
)。Name
对应于证书的特定主题。
示例:部署到 Azure 应用服务
此部分介绍如何使用存储在证书存储中的证书将应用部署到 Azure 应用服务。 若要修改应用以从证书存储加载证书,在以后的步骤中在 Azure 门户中配置应用时,需要标准层服务计划或更好的计划。
在应用的 appsettings.json
文件中,修改 IdentityServer
部分以包含密钥详细信息:
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "My",
"StoreLocation": "CurrentUser",
"Name": "CN=MyApplication"
}
}
- 证书存储区的名称表示存放证书的存储库的名称。 在此例中,它指向个人用户存储。
- 存储位置表示从中加载证书的位置(
CurrentUser
或LocalMachine
)。 - 证书中的名称属性对应于证书的可分辨主题。
若要部署到 Azure 应用服务,请按照将应用部署到 Azure 中的步骤进行操作,其中说明了如何创建所需 Azure 资源并将应用部署到生产。
按照以上说明进行操作后,应用会部署到 Azure,但尚无法正常运行。 应用使用的证书必须在 Azure 门户进行配置。 找到证书的指纹,并按照加载证书中所述的步骤进行操作。
虽然这些步骤涉及 SSL,但 Azure 门户中有一个“私有证书”部分,可在其中上传预配的证书以用于应用。
在 Azure 门户中配置应用和应用的设置之后,在门户中重启应用。
其他配置选项
对 API 授权的支持基于 IdentityServer,其中具有一组可简化 SPA 体验的约定、默认值和增强功能。 毋庸讳言,如果 ASP.NET Core 集成未涵盖你的方案,则可在后台使用 IdentityServer 的完整功能。 ASP.NET Core 支持侧重于“第一方”应用,其中所有应用都由我们的组织进行创建和部署。 因此,没有为同意或联合等内容提供支持。 对于这些方案,请使用 IdentityServer 并遵循其文档。
应用程序配置文件
应用程序配置文件是应用的预定义配置,可进一步定义其参数。 目前支持以下配置文件:
IdentityServerSPA
:将与 IdentityServer 一起托管的 SPA 表示为单个单元。redirect_uri
默认为/authentication/login-callback
。post_logout_redirect_uri
默认为/authentication/logout-callback
。- 范围集包括
openid
、profile
以及为应用中的 API 定义的每个范围。 - 允许的 OIDC 响应类型集是
id_token token
或单独每个类型(id_token
、token
)。 - 允许的响应模式为
fragment
。
SPA
:表示未与 IdentityServer 一起托管的 SPA。- 范围集包括
openid
、profile
以及为应用中的 API 定义的每个范围。 - 允许的 OIDC 响应类型集是
id_token token
或单独每个类型(id_token
、token
)。 - 允许的响应模式为
fragment
。
- 范围集包括
IdentityServerJwt
:表示与 IdentityServer 一起托管的 API。- 应用配置为具有默认为应用名称的单个范围。
API
:表示未与 IdentityServer 一起托管的 API。- 应用被配置为具有一个默认权限范围,该范围默认为应用名称。
通过 AppSettings
进行的配置
可通过配置系统配置应用,具体方法是将它们添加到 Clients
或 Resources
的列表。
配置每个客户端的 redirect_uri
和 post_logout_redirect_uri
属性,如下面的示例所示:
"IdentityServer": {
"Clients": {
"MySPA": {
"Profile": "SPA",
"RedirectUri": "https://www.example.com/authentication/login-callback",
"LogoutUri": "https://www.example.com/authentication/logout-callback"
}
}
}
配置资源时,可以配置资源的范围,如下所示:
"IdentityServer": {
"Resources": {
"MyExternalApi": {
"Profile": "API",
"Scopes": "a b c"
}
}
}
通过代码进行的配置
还可使用执行操作来配置选项的 AddApiAuthorization
的重载,通过代码配置客户端和资源。
AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddSPA(
"My SPA", spa =>
spa.WithRedirectUri("http://www.example.com/authentication/login-callback")
.WithLogoutRedirectUri(
"http://www.example.com/authentication/logout-callback"));
options.ApiResources.AddApiResource("MyExternalApi", resource =>
resource.WithScopes("a", "b", "c"));
});
其他资源
ASP.NET Core 3.1 及更高版本模板使用 API 授权支持在单页应用 (SPA) 中提供身份验证。 用于验证和存储用户身份信息的 ASP.NET Core Identity 与用于实现 Open ID Connect 的 IdentityServer 结合使用。
一个身份验证参数已添加到 Angular 和 React 项目模板,该参数类似于 Web 应用(模型-视图-控制器)(MVC) 和 Web 应用 ( Pages) 项目模板中的身份验证参数。 允许的参数值为 None 和 Individual。 React.js 和 Redux 项目模板目前不支持身份验证参数。
使用 API 授权支持创建应用
用户身份验证和授权可以与 Angular 和 React SPA 结合使用。 打开命令行界面,然后运行以下命令:
Angular:
dotnet new angular -o <output_directory_name>
React:
dotnet new react -o <output_directory_name> -au Individual
上面的命令创建一个 ASP.NET Core 应用,它具有包含 SPA 的 ClientApp 目录。
应用的 ASP.NET Core 组件的一般说明
以下部分介绍了在包括身份验证支持的情况下对项目添加的内容:
Startup
类
下面的代码示例依赖于 Microsoft.AspNetCore.ApiAuthorization.IdentityServer NuGet 包。 该示例使用 AddApiAuthorization 和 AddIdentityServerJwt 扩展方法配置 API 身份验证和授权。 使用带身份验证的 React 或 Angular SPA 项目模板的项目包含对此包的引用。
Startup
类包含以下添加项:
在
Startup.ConfigureServices
方法中:包含默认 UI 的 Identity:
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))); services.AddDefaultIdentity<ApplicationUser>() .AddEntityFrameworkStores<ApplicationDbContext>();
包含附加
AddApiAuthorization
帮助器方法的 IdentityServer,该方法会在 IdentityServer 之上设置某些默认 ASP.NET Core 约定:services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
使用附加的
AddIdentityServerJwt
帮助器方法进行身份验证,该方法将应用配置为验证 IdentityServer 生成的 JWT 令牌。services.AddAuthentication() .AddIdentityServerJwt();
在
Startup.Configure
方法中:负责验证请求凭据并在请求上下文中设置用户的身份验证中间件:
app.UseAuthentication();
用于公开 OpenID Connect 终结点的 IdentityServer 中间件:
app.UseIdentityServer();
警告
本文介绍连接字符串的使用。 使用本地数据库时,用户无需进行身份验证,但在生产环境中,连接字符串有时包括进行身份验证的密码。 资源所有者密码凭据(ROPC)是在生产数据库中应避免的安全风险。 生产应用应使用可用的最安全的身份验证流。 有关部署到测试或生产环境的应用的身份验证的详细信息,请参阅 安全身份验证流。
Linux 上的 Azure 应用服务
对于 Linux 上的 Azure 应用服务部署,请在 Startup.ConfigureServices
中显式指定颁发者:
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
options.Authority = "{AUTHORITY}";
});
在上面的代码中,{AUTHORITY}
占位符是在进行 OpenID Connect 调用时要使用的 Authority。
示例:
options.Authority = "https://contoso-service.azurewebsites.net";
AddApiAuthorization
此帮助方法将 IdentityServer 配置为使用我们支持的配置。 IdentityServer 是一个功能强大且可扩展的框架,用于处理应用安全问题。 同时,这暴露了不必要的复杂性,尤其是在最常见的情况下。 因此,提供了一组约定和配置选项作为良好的起点。 一旦身份验证需要更改,仍可使用 IdentityServer 的完整功能自定义身份验证以满足你的需求。
AddIdentityServerJwt
此帮助程序方法将应用的策略方案配置为默认身份验证处理程序。 该策略配置为允许 Identity 处理路由到 Identity URL 空间“/Identity”中任何子路径的所有请求。 JwtBearerHandler
处理所有其他请求。 此外,此方法向 IdentityServer 注册一个默认范围为 <<ApplicationName>>API
的 <<ApplicationName>>API
API 资源,并将 JWT 持有者令牌中间件配置为验证 IdentityServer 为应用颁发的令牌。
WeatherForecastController
在文件中,请注意应用于类的 [Authorize]
属性,该属性指示用户需要基于默认策略进行授权才能访问资源。 默认授权策略恰好配置为使用默认身份验证方案,而该方案由 AddIdentityServerJwt
设置为上面提到的策略方案,从而使此类帮助程序方法配置的 JwtBearerHandler
成为对应用进行的请求的默认处理程序。
ApplicationDbContext
在文件中,请注意,在 DbContext
中使用了相同的 Identity,只不过它会扩展 ApiAuthorizationDbContext
(一个来自 IdentityDbContext
的派生程度更高的类)以包括 IdentityServer 的架构。
要获取对数据库架构的完全控制,请从其中一个可用的 IdentityDbContext
类继承,并通过对 Identity 方法调用 builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value)
来配置上下文以包括 OnModelCreating
架构。
OidcConfigurationController
在文件中,请注意为处理客户端需要使用的 OIDC 参数而预配的终结点。
appsettings.json
在项目根目录的 appsettings.json
文件中,有一个新的 IdentityServer
部分,描述已配置的客户端列表。 下例中存在一个客户端。 客户端名称对应于应用名称,并通过约定映射到 OAuth ClientId
参数。 配置文件指示正在配置的应用类型。 它在内部用于促进简化服务器配置过程的约定。 有多个配置文件可用(在应用程序配置文件部分中进行了说明)。
"IdentityServer": {
"Clients": {
"angularindividualpreview3final": {
"Profile": "IdentityServerSPA"
}
}
}
appsettings.Development.json
在项目根目录的 appsettings.Development.json
文件中,有一个 IdentityServer
部分,描述用于对令牌进行签名的密钥。 部署到生产时,密钥需要与应用一起进行预配和部署(在部署到生产部分中进行了说明)。
"IdentityServer": {
"Key": {
"Type": "Development"
}
}
Angular 应用的一般说明
Angular 模板中的身份验证和 API 授权支持位于其自己的 Angular 模块中(在 ClientApp/src/api-authorization 目录中)。 该模块由以下元素组成:
- 3 个组件:
login.component.ts
:处理应用的登录流。logout.component.ts
:处理应用的注销流程。-
login-menu.component.ts
:显示以下链接集之一的小组件:- 用户身份验证后的用户资料管理和注销链接。
- 用户未经过身份验证时的注册和登录链接。
- 一个路由防护
AuthorizeGuard
,可以添加到路由,要求用户先经过身份验证,然后才能访问路由。 - 一个 HTTP 拦截器
AuthorizeInterceptor
,在用户通过身份验证后,将访问令牌附加到针对 API 的传出 HTTP 请求中。 - 服务
AuthorizeService
处理身份验证过程的较低级别详细信息,并向应用的其余部分公开有关经过身份验证的用户的信息以供使用。 - 一个 Angular 模块,定义与应用的身份验证部分关联的路由。 它公开了登录菜单组件、拦截器、保障机制和服务组件,以供应用程序的其他部分使用。
React 应用的一般说明
React 模板中的身份验证和 API 授权支持位于 ClientApp/src/components/api-authorization 目录中。 它由以下元素组成:
- 4 个组件:
Login.js
:处理应用的登录流。Logout.js
:处理应用的注销流程。-
LoginMenu.js
:显示以下链接集之一的小组件:- 用户已经过身份验证时的用户配置文件管理和注销链接。
- 用户未经过身份验证时的注册和登录链接。
AuthorizeRoute.js
:一个路由组件,要求用户先经过身份验证,然后才能呈现Component
参数中指示的组件。
- 一个导出的类
authService
实例AuthorizeService
,用于处理身份验证过程的较低级别详细信息,并将有关经过身份验证的用户的信息公开给应用的其余部分以供使用。
现在,你已了解了解决方案的主要组件,可以更深入地了解应用的各个方案。
要求对新 API 进行授权
默认情况下,系统配置为可轻松地要求为新 API 进行授权。 为此,请创建新控制器,并将 [Authorize]
属性添加到控制器类或控制器中的任何操作。
自定义 API 身份验证处理程序
若要自定义 API 的 JWT 处理程序的配置,请配置其 JwtBearerOptions 实例:
services.AddAuthentication()
.AddIdentityServerJwt();
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
...
});
API 的 JWT 处理程序会引发相应事件,通过这些事件可使用 JwtBearerEvents
控制身份验证过程。 为了为 API 授权提供支持,AddIdentityServerJwt
会注册其自己的事件处理程序。
若要自定义事件的处理,请根据需要使用其他逻辑来包装现有事件处理程序。 例如:
services.Configure<JwtBearerOptions>(
IdentityServerJwtConstants.IdentityServerJwtBearerScheme,
options =>
{
var onTokenValidated = options.Events.OnTokenValidated;
options.Events.OnTokenValidated = async context =>
{
await onTokenValidated(context);
...
}
});
在上面的代码中,OnTokenValidated
事件处理程序替换为自定义实现。 此实现:
- 调用 API 授权支持提供的原始实现。
- 运行自己的自定义逻辑。
保护客户端路由 (Angular)
保护客户端路由的方法是,在配置路由时,将授权保障添加到要运行的保障列表中。 作为示例,可以查看如何在主应用 Angular 模块中配置 fetch-data
路由:
RouterModule.forRoot([
// ...
{ path: 'fetch-data', component: FetchDataComponent, canActivate: [AuthorizeGuard] },
])
必须指出的是,保护路由不会保护实际终结点(这仍然需要向它应用 [Authorize]
属性),而是只会阻止用户在未经过身份验证时导航到给定客户端路由。
对 API 请求进行身份验证 (Angular)
通过使用应用定义的 HTTP 客户端侦听器自动对与应用一起托管的 API 的请求进行身份验证。
保护客户端路由 (React)
使用 AuthorizeRoute
组件(而不是普通 Route
组件)保护客户端路由。 例如,请注意如何在 fetch-data
组件中配置 App
路由:
<AuthorizeRoute path='/fetch-data' component={FetchData} />
保护路由:
- 不保护实际终结点(这仍然需要向它应用
[Authorize]
属性)。 - 仅阻止用户在未经过身份验证时导航到给定客户端路由。
对 API 请求进行身份验证 (React)
使用 React 对请求进行身份验证的方式是首先从 authService
导入 AuthorizeService
实例。 访问令牌从 authService
进行检索并附加到请求,如下所示。 在 React 组件中,此工作通常在 componentDidMount
生命周期方法中完成,或是作为某种用户交互的结果。
将 authService
导入组件
import authService from './api-authorization/AuthorizeService'
检索访问令牌并将其附加到响应中
async populateWeatherData() {
const token = await authService.getAccessToken();
const response = await fetch('api/SampleData/WeatherForecasts', {
headers: !token ? {} : { 'Authorization': `Bearer ${token}` }
});
const data = await response.json();
this.setState({ forecasts: data, loading: false });
}
部署到生产
若要将应用部署到生产,需要预配以下资源:
- 用于存储 Identity 用户帐户和 IdentityServer 授权的数据库。
- 用于对令牌进行签名的生产证书。
- 此证书没有特定要求;它可以是自签名证书,或是通过 CA 颁发机构预配的证书。
- 它可以通过标准工具(如 PowerShell 或 OpenSSL)来生成。
- 它可以安装到目标计算机上的证书存储中,也可以作为具有强密码 .pfx 文件进行部署。
示例:部署到非 Azure Web 托管提供者
在 Web 托管面板中,创建或加载证书。 然后在应用的 appsettings.json
文件中,修改 IdentityServer
部分以包含密钥详细信息。 例如:
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "WebHosting",
"StoreLocation": "CurrentUser",
"Name": "CN=MyApplication"
}
}
在上面的示例中:
-
StoreName
表示存储证书的证书存储的名称。 在此例中,它指向 Web 托管商店。 StoreLocation
表示从中加载证书的位置(在此例中为CurrentUser
)。-
Name
对应于证书的可分辨主题。
示例:部署到 Azure 应用服务
此部分介绍如何使用存储在证书存储中的证书将应用部署到 Azure 应用服务。 若要修改应用以从证书存储加载证书,在以后的步骤中在 Azure 门户中配置应用时,需要标准层服务计划或更好的计划。
在应用的 appsettings.json
文件中,修改 IdentityServer
部分以包含密钥详细信息:
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "My",
"StoreLocation": "CurrentUser",
"Name": "CN=MyApplication"
}
}
- 存储库名称表示存储证书的仓库名称。 在此例中,它指向个人用户存储库。
- 存储位置表示从中加载证书的位置(
CurrentUser
或LocalMachine
)。 - 证书中的名称属性对应于证书的可分辨主题。
若要部署到 Azure 应用服务,请按照将应用部署到 Azure 中的步骤进行操作,其中说明了如何创建所需 Azure 资源并将应用部署到生产。
按照以上说明进行操作后,应用会部署到 Azure,但尚无法正常运行。 应用使用的证书必须在 Azure 门户进行配置。 找到证书的指纹,并按照加载证书中所述的步骤进行操作。
虽然这些步骤涉及 SSL,但 Azure 门户中有一个“私有证书”部分,可在其中上传预配的证书以用于应用。
在 Azure 门户中配置应用和应用的设置之后,在门户中重启应用。
其他配置选项
对 API 授权的支持基于 IdentityServer,其中具有一组可简化 SPA 体验的约定、默认值和增强功能。 毋庸讳言,如果 ASP.NET Core 集成未涵盖你的方案,则可在后台使用 IdentityServer 的完整功能。 ASP.NET Core 支持侧重于“第一方”应用,其中所有应用都由我们的组织进行创建和部署。 因此,没有为同意或联合等内容提供支持。 对于这些方案,请使用 IdentityServer 并遵循其文档。
应用程序配置文件
应用程序配置文件是应用的预定义配置,可进一步定义其参数。 目前支持以下配置文件:
IdentityServerSPA
:将与 IdentityServer 一起托管的 SPA 表示为单个单元。redirect_uri
默认为/authentication/login-callback
。post_logout_redirect_uri
默认为/authentication/logout-callback
。- 范围集包括
openid
、profile
以及为应用中的 API 定义的每个范围。 - 允许的 OIDC 响应类型集是
id_token token
或单独每个类型(id_token
、token
)。 - 允许的响应模式为
fragment
。
SPA
:表示未与 IdentityServer 一起托管的 SPA。- 范围集包括
openid
、profile
以及为应用中的 API 定义的每个范围。 - 允许的 OIDC 响应类型集是
id_token token
或单独每个类型(id_token
、token
)。 - 允许的响应模式为
fragment
。
- 范围集包括
IdentityServerJwt
:表示与 IdentityServer 一起托管的 API。- 应用配置为具有默认为应用名称的单个范围。
API
:表示未与 IdentityServer 一起托管的 API。- 应用配置为只有一个范围,该范围默认为应用名称。
通过 AppSettings
进行的配置
可通过配置系统配置应用,具体方法是将它们添加到 Clients
或 Resources
的列表。
配置每个客户端的 redirect_uri
和 post_logout_redirect_uri
属性,如下面的示例所示:
"IdentityServer": {
"Clients": {
"MySPA": {
"Profile": "SPA",
"RedirectUri": "https://www.example.com/authentication/login-callback",
"LogoutUri": "https://www.example.com/authentication/logout-callback"
}
}
}
配置资源时,可以配置资源的范围,如下所示:
"IdentityServer": {
"Resources": {
"MyExternalApi": {
"Profile": "API",
"Scopes": "a b c"
}
}
}
通过代码进行的配置
还可使用执行操作来配置选项的 AddApiAuthorization
的重载,通过代码配置客户端和资源。
AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddSPA(
"My SPA", spa =>
spa.WithRedirectUri("http://www.example.com/authentication/login-callback")
.WithLogoutRedirectUri(
"http://www.example.com/authentication/logout-callback"));
options.ApiResources.AddApiResource("MyExternalApi", resource =>
resource.WithScopes("a", "b", "c"));
});