在 ASP.NET 应用之间共享身份验证 cookie
网站通常由各个协同工作的 Web 应用组成。 若要提供单一登录 (SSO) 体验,站点内的 Web 应用必须共享身份验证 Cookie。 为了支持此方案,数据保护堆栈允许共享 Katana cookie 身份验证和 ASP.NET Core cookie 身份验证票证。
在以下示例中:
- 身份验证 cookie 名称设置为通用值
.AspNet.SharedCookie
。 AuthenticationType
显式或默认设置为Identity.Application
。- 通用应用名称
SharedCookieApp
用于使数据保护系统能够共享数据保护密钥。 Identity.Application
用作身份验证方案。 无论使用哪种方案,都必须通过以下方式在共享 cookie 应用内部及其之间始终如一地使用该方案:用作默认方案或进行显式设置。 该方案在加密和解密 Cookie 时使用,因此必须跨应用使用一致的方案。- 使用通用的数据保护密钥存储位置。
- 在 ASP.NET Core 应用中,PersistKeysToFileSystem 用于设置密钥存储位置。
- 在 .NET Framework 应用中,Cookie 身份验证中间件使用 DataProtectionProvider 的实现。
DataProtectionProvider
为身份验证 cookie 负载数据的加密和解密提供数据保护服务。DataProtectionProvider
实例与应用其他部分使用的数据保护系统隔离。 DataProtectionProvider.Create(System.IO.DirectoryInfo, Action<IDataProtectionBuilder>) 接受 DirectoryInfo 以指定数据保护密钥的存储位置。
DataProtectionProvider
需要 Microsoft.AspNetCore.DataProtection.Extensions NuGet 包:- 在 .NET Framework 应用中,添加对 Microsoft.AspNetCore.DataProtection.Extensions 的包引用。
- SetApplicationName 设置通用应用名称。
与 ASP.NET Core Identity 共享身份验证 Cookie
使用 ASP.NET Core Identity时:
- 必须在应用之间共享数据保护密钥和应用名称。 以下示例为 PersistKeysToFileSystem 方法提供了一个通用密钥存储位置。 使用 SetApplicationName 配置一个通用共享应用名称(以下示例中的
SharedCookieApp
)。 有关详细信息,请参阅配置 ASP.NET Core 数据保护。 - 使用 ConfigureApplicationCookie 扩展方法设置 Cookie 的数据保护服务。
- 默认身份验证类型为
Identity.Application
。
在 Program.cs
中:
using Microsoft.AspNetCore.DataProtection;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"c:\PATH TO COMMON KEY RING FOLDER"))
.SetApplicationName("SharedCookieApp");
builder.Services.ConfigureApplicationCookie(options => {
options.Cookie.Name = ".AspNet.SharedCookie";
});
var app = builder.Build();
注意:上述说明不适用于 ITicketStore
(CookieAuthenticationOptions.SessionStore
)。 有关详细信息,请参阅此 GitHub 问题。
出于安全原因,ASP.NET Core 中不压缩身份验证 Cookie。 使用身份验证 Cookie 时,开发人员应将声明信息数量减少到所需的量。
在没有 ASP.NET Core Identity 的情况下共享身份验证 Cookie
在没有 ASP.NET Core Identity 的情况下直接使用 Cookie 时,请配置数据保护和身份验证。 在以下示例中,身份验证类型设置为 Identity.Application
:
using Microsoft.AspNetCore.DataProtection;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"c:\PATH TO COMMON KEY RING FOLDER"))
.SetApplicationName("SharedCookieApp");
builder.Services.AddAuthentication("Identity.Application")
.AddCookie("Identity.Application", options =>
{
options.Cookie.Name = ".AspNet.SharedCookie";
});
var app = builder.Build();
出于安全原因,ASP.NET Core 中不压缩身份验证 Cookie。 使用身份验证 Cookie 时,开发人员应将声明信息数量减少到所需的量。
跨不同的基路径共享 Cookie
身份验证cookie使用HttpRequest.PathBase作为其默认的Cookie.Path。 如果必须跨不同的基本路径共享应用 cookie,则必须替代 Path
:
using Microsoft.AspNetCore.DataProtection;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"c:\PATH TO COMMON KEY RING FOLDER"))
.SetApplicationName("SharedCookieApp");
builder.Services.ConfigureApplicationCookie(options => {
options.Cookie.Name = ".AspNet.SharedCookie";
options.Cookie.Path = "/";
});
var app = builder.Build();
跨不同子域共享 Cookie
当托管跨子域共享 Cookie 的应用时,在 Cookie.Domain 属性中指定公共域。 若要在 contoso.com
上跨应用共享 Cookie,例如 first_subdomain.contoso.com
和 second_subdomain.contoso.com
,请将 Cookie.Domain
指定为 .contoso.com
:
options.Cookie.Domain = ".contoso.com";
rest 加密数据保护密钥
对于生产部署,请将 DataProtectionProvider
配置为使用 DPAPI 或 X509Certificate 对密钥进行 rest 加密。 有关详细信息,请参阅使用 ASP.NET Core 在 Windows 和 Azure 中实现处于 rest 的密钥加密。 在以下示例中,系统向 ProtectKeysWithCertificate 提供了证书指纹:
using Microsoft.AspNetCore.DataProtection;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDataProtection()
.ProtectKeysWithCertificate("{CERTIFICATE THUMBPRINT}");
使用通用用户数据库
当应用使用相同的 Identity 架构(相同版本的 Identity)时,请确认每个应用的 Identity 系统均指向相同的用户数据库。 否则,当 identity 系统尝试将身份验证 cookie 中的信息与其数据库中的信息进行匹配时,就会在运行时产生故障。
当应用之间的 Identity 架构不同时(通常是因为应用使用不同的 Identity 版本),如果不在其他应用的 Identity 中重新映射和添加列,就不可能共享基于最新版 Identity 的通用数据库。 将其他应用升级为使用最新 Identity 版本通常更有效,这样应用就可以共享一个通用数据库。
应用程序名称更改
在 .NET 6 中,WebApplicationBuilder 会将内容根路径规范化以 DirectorySeparatorChar 结尾。 大多数从HostBuilder或WebHostBuilder迁移的应用不会共享相同的应用名称,因为它们没有规范化。 有关详细信息,请参阅SetApplicationName
在 ASP.NET 4.x 和 ASP.NET Core 应用之间共享身份验证 Cookie
使用 Microsoft.Owin Cookie 身份验证中间件的 ASP.NET 4.x 应用可配置为生成与 ASP.NET Core Cookie 身份验证中间件兼容的身份验证 Cookie。 如果 Web 应用程序包含必须共享单一登录体验的 ASP.NET 4.x 应用和 ASP.NET Core 应用,这非常有用。 此类场景的具体示例是以增量方式将 Web 应用从 ASP.NET 迁移到 ASP.NET Core。 在这种情况下,应用的一些部分通常由原始 ASP.NET 应用提供,而其他部分由新的 ASP.NET Core 应用提供。 不过,用户只需登录一次。 这可以使用以下方法之一完成:
- 使用 System.Web 适配器的远程身份验证功能,该功能使用 ASP.NET 应用将用户登录。
- 将 ASP.NET 应用配置为使用 Microsoft.Owin Cookie 身份验证中间件,以便与 ASP.NET Core 应用共享身份验证 Cookie。
要将 ASP.NET Microsoft.Owin Cookie 身份验证中间件配置为与 ASP.NET Core 应用共享 Cookie,请按照前面的说明将 ASP.NET Core 应用配置为使用特定的 cookie 名称、应用名称,并将数据保护密钥保存到已知位置。 有关保留数据保护密钥的详细信息,请参阅配置 ASP.NET Core 数据保护。
在 ASP.NET 应用中,安装Microsoft.Owin.Security.Interop
包。
更新 Startup.Auth.cs 中的UseCookieAuthentication
调用,以将 AspNetTicketDataFormat 配置为匹配 ASP.NET Core 应用的设置:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
},
// Settings to configure shared cookie with ASP.NET Core app
CookieName = ".AspNet.ApplicationCookie",
AuthenticationType = "Identity.Application",
TicketDataFormat = new AspNetTicketDataFormat(
new DataProtectorShim(
DataProtectionProvider.Create(new DirectoryInfo(@"c:\PATH TO COMMON KEY RING FOLDER"),
builder => builder.SetApplicationName("SharedCookieApp"))
.CreateProtector(
"Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware",
// Must match the Scheme name used in the ASP.NET Core app, i.e. IdentityConstants.ApplicationScheme
"Identity.Application",
"v2"))),
CookieManager = new ChunkingCookieManager()
});
此处配置的重要项目包括:
- cookie名称设置为与 ASP.NET Core 应用中的名称相同。
- 使用同一密钥环路径创建数据保护提供程序。 请注意,在这些示例中,数据保护密钥存储在磁盘上,但可以使用其他数据保护提供程序。 例如,只要配置在应用之间匹配,就可以将 Redis 或 Azure Blob 存储用于数据保护提供程序。 有关保留数据保护密钥的详细信息,请参阅配置 ASP.NET Core 数据保护。
- 应用名称设置为与 ASP.NET Core 应用中使用的应用名称相同。
- 身份验证类型设置为 ASP.NET Core 应用中身份验证方案的名称。
System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifier
设置为来自 ASP.NET Core identity 的声明,该标识对用户是唯一的。
由于身份验证类型已更改以匹配 ASP.NET Core 应用的身份验证方案,因此还需要更新 ASP.NET 应用生成新标识以使用该相同名称的方式。 一般在Models/IdentityModels.cs
中执行此操作:
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, "Identity.Application");
// Add custom user claims here
return userIdentity;
}
}
通过这些更改,ASP.NET 和 ASP.NET Core 应用能够使用相同的身份验证 Cookie,以便登录或注销一个应用的用户反映在另一个应用中。
请注意,由于 ASP.NET Identity和 ASP.NET Core Identity的数据库架构之间存在差异,因此建议用户仅使用其中一个应用(ASP.NET 或 ASP.NET Core 应用)登录。 用户登录后,本部分中所述的步骤将允许任一应用使用身份验证cookie,并且两个应用都应能够注销用户。
其他资源
在以下示例中:
- 身份验证 cookie 名称设置为通用值
.AspNet.SharedCookie
。 AuthenticationType
显式或默认设置为Identity.Application
。- 通用应用名称用于使数据保护系统能够共享数据保护密钥 (
SharedCookieApp
)。 Identity.Application
用作身份验证方案。 无论使用哪种方案,都必须通过以下方式在共享 cookie 应用内部及其之间始终如一地使用该方案:用作默认方案或进行显式设置。 该方案在加密和解密 Cookie 时使用,因此必须跨应用使用一致的方案。- 使用通用的数据保护密钥存储位置。
- 在 ASP.NET Core 应用中,PersistKeysToFileSystem 用于设置密钥存储位置。
- 在 .NET Framework 应用中,Cookie 身份验证中间件使用 DataProtectionProvider 的实现。
DataProtectionProvider
为身份验证 cookie 负载数据的加密和解密提供数据保护服务。DataProtectionProvider
实例与应用其他部分使用的数据保护系统隔离。 DataProtectionProvider.Create(System.IO.DirectoryInfo, Action<IDataProtectionBuilder>) 接受 DirectoryInfo 以指定数据保护密钥的存储位置。
DataProtectionProvider
需要 Microsoft.AspNetCore.DataProtection.Extensions NuGet 包:- 在 ASP.NET Core 2.x 应用中,引用 Microsoft.AspNetCore.App 元包。
- 在 .NET Framework 应用中,添加对 Microsoft.AspNetCore.DataProtection.Extensions 的包引用。
- SetApplicationName 设置通用应用名称。
与 ASP.NET Core Identity 共享身份验证 Cookie
使用 ASP.NET Core Identity时:
- 必须在应用之间共享数据保护密钥和应用名称。 以下示例为 PersistKeysToFileSystem 方法提供了一个通用密钥存储位置。 使用 SetApplicationName 配置一个通用共享应用名称(以下示例中的
SharedCookieApp
)。 有关详细信息,请参阅配置 ASP.NET Core 数据保护。 - 使用 ConfigureApplicationCookie 扩展方法设置 Cookie 的数据保护服务。
- 默认身份验证类型为
Identity.Application
。
在 Startup.ConfigureServices
中:
services.AddDataProtection()
.PersistKeysToFileSystem("{PATH TO COMMON KEY RING FOLDER}")
.SetApplicationName("SharedCookieApp");
services.ConfigureApplicationCookie(options => {
options.Cookie.Name = ".AspNet.SharedCookie";
});
注意:上述说明不适用于 ITicketStore
(CookieAuthenticationOptions.SessionStore
)。 有关详细信息,请参阅此 GitHub 问题。
出于安全原因,ASP.NET Core 中不压缩身份验证 Cookie。 使用身份验证 Cookie 时,开发人员应将声明信息数量减少到所需的量。
在没有 ASP.NET Core Identity 的情况下共享身份验证 Cookie
在没有 ASP.NET Core Identity 的情况下直接使用 Cookie 时,请在 Startup.ConfigureServices
中配置数据保护和身份验证。 在以下示例中,身份验证类型设置为 Identity.Application
:
services.AddDataProtection()
.PersistKeysToFileSystem("{PATH TO COMMON KEY RING FOLDER}")
.SetApplicationName("SharedCookieApp");
services.AddAuthentication("Identity.Application")
.AddCookie("Identity.Application", options =>
{
options.Cookie.Name = ".AspNet.SharedCookie";
});
出于安全原因,ASP.NET Core 中不压缩身份验证 Cookie。 使用身份验证 Cookie 时,开发人员应将声明信息数量减少到所需的量。
跨不同的基路径共享 Cookie
身份验证cookie使用HttpRequest.PathBase作为其默认的Cookie.Path。 如果必须跨不同的基本路径共享应用 cookie,则必须替代 Path
:
services.AddDataProtection()
.PersistKeysToFileSystem("{PATH TO COMMON KEY RING FOLDER}")
.SetApplicationName("SharedCookieApp");
services.ConfigureApplicationCookie(options => {
options.Cookie.Name = ".AspNet.SharedCookie";
options.Cookie.Path = "/";
});
跨不同子域共享 Cookie
当托管跨子域共享 Cookie 的应用时,在 Cookie.Domain 属性中指定公共域。 若要在 contoso.com
上跨应用共享 Cookie,例如 first_subdomain.contoso.com
和 second_subdomain.contoso.com
,请将 Cookie.Domain
指定为 .contoso.com
:
options.Cookie.Domain = ".contoso.com";
rest 加密数据保护密钥
对于生产部署,请将 DataProtectionProvider
配置为使用 DPAPI 或 X509Certificate 对密钥进行 rest 加密。 有关详细信息,请参阅使用 ASP.NET Core 在 Windows 和 Azure 中实现处于 rest 的密钥加密。 在以下示例中,系统向 ProtectKeysWithCertificate 提供了证书指纹:
services.AddDataProtection()
.ProtectKeysWithCertificate("{CERTIFICATE THUMBPRINT}");
在 ASP.NET 4.x 和 ASP.NET Core 应用之间共享身份验证 Cookie
可以将使用 Katana Cookie 身份验证中间件的 ASP.NET 4.x 应用配置为生成与 ASP.NET Core Cookie 身份验证中间件兼容的身份验证 Cookie。 有关详细信息,请参阅在 ASP.NET 4.x 和 ASP.NET Core 应用 (dotnet/AspNetCore.Docs #21987) 之间共享身份验证 Cookie。
使用通用用户数据库
当应用使用相同的 Identity 架构(相同版本的 Identity)时,请确认每个应用的 Identity 系统均指向相同的用户数据库。 否则,当 identity 系统尝试将身份验证 cookie 中的信息与其数据库中的信息匹配时,会在运行时生成故障。
当应用之间的 Identity 架构不同时(通常是因为应用使用不同的 Identity 版本),如果不在其他应用的 Identity 中重新映射和添加列,就不可能共享基于最新版 Identity 的通用数据库。 将其他应用升级为使用最新 Identity 版本通常更有效,这样应用就可以共享一个通用数据库。