在 ASP.NET 應用程式之間共用驗證 cookie

作者:Rick Anderson

網站通常由個別 Web 應用程式共同運作所組成。 若要提供單一登入 (SSO) 體驗,網站內的 Web 應用程式必須共用驗證 cookie。 為了支援此案例,資料保護堆疊允許共用 Katana cookie 驗證和 ASP.NET Core cookie 驗證票證。

在下列範例中:

  • 驗證 cookie 名稱設定為 .AspNet.SharedCookie 的一般值。
  • AuthenticationType 會明確或預設設定為 Identity.Application
  • 使用常見的應用程式名稱 SharedCookieApp 可讓資料保護系統共用資料保護金鑰。
  • Identity.Application 用作驗證配置。 無論使用何種配置,它都必須一致地在共用 cookie 應用程式內部和應用程式之間做為預設配置或明確設定。 會在加密和解密 cookie 時使用配置,因此必須在應用程式之間使用一致的配置。
  • 使用一般資料保護金鑰儲存位置。
  • DataProtectionProvider 需要 Microsoft.AspNetCore.DataProtection.Extensions NuGet 套件:
  • SetApplicationName 設定一般應用程式名稱。

共用包含 ASP.NET Core Identity 的驗證 cookie

使用 ASP.NET Core Identity 時:

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.comsecond_subdomain.contoso.com),請將 Cookie.Domain 指定為 .contoso.com

options.Cookie.Domain = ".contoso.com";

加密待用資料保護金鑰

針對生產部署,請使用 DPAPI 或 X509Certificate 將 DataProtectionProvider 設定為加密待用金鑰。 如需詳細資訊,請參閱使用 ASP.NET Core 在 Windows 和 Azure 中加密待用金鑰。 在下列範例中,會將憑證指紋提供給 ProtectKeysWithCertificate

using Microsoft.AspNetCore.DataProtection;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddDataProtection()
    .ProtectKeysWithCertificate("{CERTIFICATE THUMBPRINT}");

使用一般使用者資料庫

當應用程式使用相同的 Identity 架構 (相同版本的 Identity),請確認每個應用程式的 Identity 系統都指向相同的使用者資料庫。 否則,當身分識別系統嘗試將驗證 cookie 中的資訊與資料庫中的資訊比較時,會在執行階段產生失敗。

當 Identity 架構在應用程式之間不同時,通常是因為應用程式使用了不同的 Identity 版本,因此如果不重新對應並在其他應用程式 Identity 架構中新增資料行,便無法根據最新版本的 Identity 共用通用資料庫。 更高效的做法是升級其他應用程式以使用最新的 Identity 版本,讓應用程式可以共用通用資料庫。

應用程式名稱變更

在 .NET 6 中,WebApplicationBuilder 會將內容根路徑正規化,使其以 DirectorySeparatorChar 結尾。 大部分從 HostBuilderWebHostBuilder 移轉的應用程式都不會有相同的應用程式名稱,因為這些應用程式並未正規化。 如需詳細資訊,請參閱 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 驗證中介軟體,以便驗證 cookie 與 ASP.NET Core 應用程式共用。

若要設定 ASP.NET Microsoft.Owin Cookie 驗證中介軟體,以與 ASP.NET Core 應用程式共用 cookie,請遵循上述指示,將 ASP.NET Core 應用程式設定為使用特定 cookie 名稱、應用程式名稱,而且將資料保護金鑰保存至已知位置。 如需保存資料保護金鑰的詳細資訊,請參閱設定 ASP.NET 核心資料保護

在 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 核心資料保護
  • 應用程式名稱設定為與 ASP.NET Core 應用程式中所使用的應用程式名稱相同。
  • 驗證類型會設定為 ASP.NET Core 應用程式中的驗證配置名稱。
  • System.Web.Helpers.AntiForgeryConfig.UniqueClaimTypeIdentifier 會設定使用者獨有的 ASP.NET Core 身分識別的宣告。

由於驗證類型已變更為符合 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 由任一應用程式使用,而且這兩個應用程式都應該能夠登出使用者。

其他資源

在下列範例中:

共用包含 ASP.NET Core Identity 的驗證 cookie

使用 ASP.NET Core Identity 時:

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.comsecond_subdomain.contoso.com),請將 Cookie.Domain 指定為 .contoso.com

options.Cookie.Domain = ".contoso.com";

加密待用資料保護金鑰

針對生產部署,請使用 DPAPI 或 X509Certificate 將 DataProtectionProvider 設定為加密待用金鑰。 如需詳細資訊,請參閱使用 ASP.NET Core 在 Windows 和 Azure 中加密待用金鑰。 在下列範例中,會將憑證指紋提供給 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 應用程式之間共用驗證 cookie(dotnet/AspNetCore.Docs #21987)

使用一般使用者資料庫

當應用程式使用相同的 Identity 架構 (相同版本的 Identity),請確認每個應用程式的 Identity 系統都指向相同的使用者資料庫。 否則,當身分識別系統嘗試將驗證 cookie 中的資訊與資料庫中的資訊比較時,會在執行階段產生失敗。

當 Identity 架構在應用程式之間不同時,通常是因為應用程式使用了不同的 Identity 版本,因此如果不重新對應並在其他應用程式 Identity 架構中新增資料行,便無法根據最新版本的 Identity 共用通用資料庫。 更高效的做法是升級其他應用程式以使用最新的 Identity 版本,讓應用程式可以共用通用資料庫。

其他資源