将 ASP.NET 框架身份验证迁移到 ASP.NET Core

身份验证是 Web 应用程序的关键组件,可跨 HTTP 请求处理用户身份验证。 从 ASP.NET Framework 迁移到 ASP.NET Core 时,身份验证会带来独特的挑战,因为两个框架处理身份验证的方式大相径庭。

有关 ASP.NET Core 中的身份验证的一般信息,请参阅 ASP.NET 核心身份验证概述。 有关授权的信息,请参阅 ASP.NET Core 中的授权简介

为什么身份验证迁移很复杂

ASP.NET Framework 和 ASP.NET Core 具有基本不同的身份验证方法:

  • ASP.NET Framework 为内置身份验证模块提供自动配置,并与 System.Web 紧密集成
  • ASP.NET Core 使用基于中间件的方法进行显式配置和依赖项注入

这些差异意味着,您无法无需更改地将身份验证代码从框架移动到 Core。

身份验证类型和迁移挑战

不同的身份验证类型提供不同级别的迁移复杂性:

  • ASP.NET 框架:使用 Microsoft.Owincookie 身份验证中间件
  • ASP.NET Core:使用不同配置的 CookieAuthentication 中间件
  • 迁移挑战: Cookie 格式、加密密钥和配置差异

JWT 持有者令牌

  • ASP.NET 框架:通常通过自定义模块或 OWIN 中间件进行处理
  • ASP.NET Core:原生支持Microsoft.AspNetCore.Authentication.JwtBearer
  • 迁移挑战:令牌验证参数差异和中间件注册

Windows 身份验证

  • ASP.NET Framework:内置 IIS 集成
  • ASP.NET 核心:需要显式配置,可能需要托管模型更改
  • 迁移挑战:服务器配置和身份验证流差异

窗体身份验证

  • ASP.NET Framework:内置 System.Web.Security.FormsAuthentication
  • ASP.NET 核心:无需直接等效项,需要迁移到 cookie 身份验证
  • 迁移挑战:需要将身份验证系统彻底重新设计

自定义身份验证

  • ASP.NET 框架:实现的自定义模块 IHttpModule
  • ASP.NET 核心:自定义中间件或身份验证处理程序
  • 迁移挑战:完成从模块到中间件的体系结构更改

迁移策略概述

在迁移过程中,有三种处理身份验证的主要方法:

  1. 完全重写 - 重写所有身份验证代码以使用 ASP.NET Core 的本机身份验证
  2. 远程身份验证 - 使用 System.Web 适配器将身份验证委托给 ASP.NET Framework 应用
  3. 共享 cookie 身份验证 - 在框架和核心应用程序之间共享身份验证 Cookie(适用于基于 OWIN 的方案)

对于大多数应用程序,迁移到本机 ASP.NET 核心身份验证可提供最佳性能和可维护性。 但是,较大的应用程序或具有复杂身份验证要求的应用程序可能受益于使用 System.Web 适配器的增量方法。

选择迁移方法

有三个主要选项用于将身份验证从 ASP.NET Framework 迁移到 ASP.NET Core。 选择取决于身份验证类型、迁移时间线、是否需要同时运行这两个应用程序,以及你愿意重写的代码量。

快速决策指南

回答以下问题以选择你的方法:

  1. 是否执行完全重写或增量迁移?

  2. ASP.NET Framework 应用使用的身份验证类型是什么?

    • OWIN cookie 身份验证→继续问题 3
    • 表单身份验证、JWT 持有者令牌、Windows 身份验证或自定义身份验证→ 远程身份验证
  3. ASP.NET Framework 和 ASP.NET Core 应用是否需要访问相同的身份验证状态?

  4. 是否可以在两个应用之间配置匹配的数据保护设置?

迁移方法比较

方法 代码更改 性能 身份验证共享 何时使用
完全重写 高 - 重写所有身份验证代码 最佳 没有 完整重写、性能关键型应用、非 OWIN 身份验证
远程身份验证 低 - 保留现有模式 一般 完整 增量迁移、复杂身份验证、Windows 身份验证
共享 Cookie 中等 - 更新配置 完整 OWIN cookie 身份验证,性能关键型共享身份验证(请参阅“替代项”

完全重写为 ASP.NET Core 身份验证

执行完整迁移、使用非 OWIN 身份验证或希望获得最佳性能和可维护性时,请选择此方法。

ASP.NET Core 通过高性能和广泛的自定义选项提供全面的身份验证支持。 此方法需要重写身份验证代码,但长期提供最大的优势。

完整重写的优缺点

优点 缺点
最佳性能和安全性 需要重写所有身份验证代码
本机 ASP.NET Core 实现 无自动迁移路径
完全控制身份验证流 新身份验证模式的学习曲线
没有其他依赖项 框架模式的重大更改
访问最新 ASP.NET 核心身份验证功能 迁移期间的潜在停机时间

迁移注意事项

迁移到本机 ASP.NET 核心身份验证时:

根据框架身份验证类型进行选择:

需要更改代码:

  • HttpContext.User 访问模式替换为(大致兼容)
  • Program.cs 中更新身份验证中间件注册
  • 将自定义身份验证逻辑迁移到 ASP.NET 核心模式
  • 更新授权属性和策略

何时选择此方法:

  • 可以负担得起重写与身份验证相关的代码
  • 性能是首要任务
  • 你未与旧版应用程序共享身份验证状态
  • 想要完全消除 System.Web 依赖项
  • 您的框架应用程序使用表单身份验证、自定义模块或 JWT 令牌

远程身份验证

注释

这利用 System.Web 适配器 来简化迁移。

如果需要在增量迁移期间在 ASP.NET Framework 和 ASP.NET Core 应用程序之间共享身份验证,或者很难迁移的复杂身份验证,请选择此方法。

System.Web 适配器的远程身份验证功能允许 ASP.NET 核心应用通过延迟到 ASP.NET 应用来确定用户的标识。 这样就可以在维护单个身份验证系统的同时逐步迁移。

远程身份验证的工作原理

  1. 当 ASP.NET 核心应用处理请求时,如果远程应用身份验证是默认方案或请求终结点指定的, RemoteAuthenticationAuthHandler 则会尝试对用户进行身份验证。
  2. 处理程序向 ASP.NET 应用的身份验证终结点发出 HTTP 请求,从当前请求转发配置的标头(默认情况下 AuthorizationCookie 标头)。
  3. ASP.NET 应用处理身份验证,并返回指示失败的序列化 ClaimsPrincipal 或 HTTP 状态代码。
  4. ASP.NET Core 应用使用结果来建立用户的标识或处理身份验证质询。

远程身份验证优点和缺点

优点 缺点
所需的最少代码更改 其他 HTTP 请求开销
适用于任何框架身份验证类型 应用之间的网络依赖关系
渐进迁移功能 更复杂的调试
保留现有身份验证逻辑 要求两个应用都在运行
处理复杂的身份验证方案 有限的 Windows 身份验证支持

何时选择远程身份验证

在以下情况下选择远程身份验证:

  • ASP.NET 应用不使用 Microsoft.Owincookie 身份验证
  • 你希望一个灵活的解决方案,它适用于各种身份验证机制
  • 需要逐步将身份验证逻辑迁移到 ASP.NET Core
  • 你拥有难以重写的复杂自定义身份验证
  • 正在执行增量迁移,需要共享身份验证状态

远程身份验证配置

要在已根据 入门设置的解决方案中启用远程身份验证,只需进行一些小的代码更改。

首先,按照 远程应用设置 说明连接 ASP.NET Core 和 ASP.NET 应用。 然后,只需调用几个额外的扩展方法来启用远程应用身份验证。

ASP.NET 应用配置

需要将 ASP.NET 应用配置为添加身份验证终结点。 通过调用 AddAuthenticationServer 扩展方法设置 HTTP 模块来监视对身份验证终结点的请求,可以添加身份验证终结点。 请注意,远程身份验证方案通常还需要添加代理支持,以便任何与身份验证相关的重定向会正确路由到 ASP.NET Core 应用,而不是 ASP.NET 应用。

SystemWebAdapterConfiguration.AddSystemWebAdapters(this)
    .AddProxySupport(options => options.UseForwardedHeaders = true)
    .AddRemoteAppServer(options =>
    {
        options.ApiKey = ConfigurationManager.AppSettings["RemoteAppApiKey"];
    })
    .AddAuthenticationServer();

ASP.NET 核心应用配置

接下来,需要将 ASP.NET Core 应用配置为启用身份验证处理程序,该处理程序将通过向 ASP.NET 应用发出 HTTP 请求来对用户进行身份验证。 再次强调,注册 System.Web 适配器服务时,通过调用 AddAuthenticationClient 来完成此操作。

builder.Services.AddSystemWebAdapters()
    .AddRemoteAppClient(options =>
    {
        options.RemoteAppUrl = new Uri(builder.Configuration
            ["ReverseProxy:Clusters:fallbackCluster:Destinations:fallbackApp:Address"]);
        options.ApiKey = builder.Configuration["RemoteAppApiKey"];
    })
    .AddAuthenticationClient(true);

传递给调用的 AddAuthenticationClient 布尔值指定远程应用身份验证是否应为默认身份验证方案。 通过传递 true,用户将能够通过远程应用身份验证对所有请求进行验证,而传递 false 则意味着,仅当明确请求远程应用方案时(例如,在 [Authorize(AuthenticationSchemes = RemoteAppAuthenticationDefaults.AuthenticationScheme)] 控制器或操作方法上),用户才会使用远程应用身份验证进行验证。 对此参数传递 false 的优点是仅对需要远程应用身份验证的终结点向原始 ASP.NET 应用发出 HTTP 请求进行身份验证,但其缺点是需要对所有这些终结点进行批注,以指示它们将使用远程应用身份验证。

使用 Aspire 时,配置将通过环境变量完成,并由 AppHost 设置。 若要启用远程会话,必须启用该选项:

...

var coreApp = builder.AddProject<Projects.AuthRemoteIdentityCore>("core")
    .WithHttpHealthCheck()
    .WaitFor(frameworkApp)
    .WithIncrementalMigrationFallback(frameworkApp, options => options.RemoteAuthentication = RemoteAuthentication.DefaultScheme);

...

完成此作后,它将自动挂接到框架和核心应用程序中。

对特定终结点使用远程身份验证

将默认方案 false设置为时,可以为特定控制器或作指定远程身份验证:

[Authorize(AuthenticationSchemes = RemoteAppAuthenticationDefaults.AuthenticationScheme)]
public class SecureController : Controller
{
    // This controller uses remote authentication
    public IActionResult Index()
    {
        return View();
    }
}

// Or on specific actions
public class HomeController : Controller
{
    [Authorize(AuthenticationSchemes = RemoteAppAuthenticationDefaults.AuthenticationScheme)]
    public IActionResult SecureAction()
    {
        return View();
    }
}

实现自定义身份验证结果处理器

可以实现自定义处理器,以在身份验证结果被使用之前对其进行修改。

public class CustomAuthResultProcessor : IRemoteAuthenticationResultProcessor
{
    public Task ProcessAsync(RemoteAuthenticationResult result, HttpContext context)
    {
        // Custom logic to process authentication results
        if (result.Headers.ContainsKey("Location"))
        {
            // Modify redirect URLs or other logic
        }
        
        return Task.CompletedTask;
    }
}

// Register the custom processor
builder.Services.AddScoped<IRemoteAuthenticationResultProcessor, CustomAuthResultProcessor>();

最后,如果 ASP.NET Core 应用以前不包含身份验证中间件,则需要启用该中间件(路由中间件之后,但在授权中间件之前)。 有关中间件排序的详细信息,请参阅 ASP.NET 核心中间件

app.UseAuthentication();

安全注意事项

实现远程身份验证时,请考虑以下安全方面:

  • API 密钥安全性:应使用 配置提供程序 (而不是硬编码)安全地存储用于 ASP.NET 核心和 ASP.NET 应用之间的通信的 API 密钥。
  • 网络安全:应用之间的通信应在生产环境中通过安全通道(HTTPS)进行。
  • 标头转发:请谨慎转发哪些标头以避免公开敏感信息。 仅转发标头对于身份验证是必需的。
  • Endpoint Protection:ASP.NET 应用上的身份验证终结点只能访问 ASP.NET Core 应用,而不是外部客户端。

故障排除

配置远程身份验证时的常见问题:

  • 身份验证失败:验证 API 密钥是否在两个应用程序之间匹配,以及身份验证终结点路径是否已正确配置。
  • 重定向循环:确保 RedirectUrlProcessor 正确配置为重定向到 ASP.NET Core 应用,而不是 ASP.NET 应用。
  • 缺少声明:验证 ASP.NET 应用是否正确序列化 ClaimsPrincipal 并包含所有必需的声明。

设计

  1. 当 ASP.NET 核心应用处理请求时,如果远程应用身份验证是默认方案或请求终结点指定的, RemoteAuthenticationAuthHandler 则会尝试对用户进行身份验证。
    1. 处理程序将向 ASP.NET 应用的身份验证终结点发出 HTTP 请求。 它将配置的标头从当前请求复制到此新请求,以便转发与身份验证相关的数据。 如上所述,默认行为是复制 AuthorizationCookie 标头。 此外,还会添加 API 密钥标头以实现安全目的。
  2. ASP.NET 应用将为发送到身份验证终结点的请求提供服务。 只要 API 密钥匹配,ASP.NET 应用就会返回当前用户的 ClaimsPrincipal 序列化为响应正文,或者它将返回 HTTP 状态代码(如 401 或 302)和指示失败的响应标头。
  3. 当 ASP.NET Core 应用 RemoteAuthenticationAuthHandler 收到来自 ASP.NET 应用的响应时:
    1. 如果成功返回 ClaimsPrincipal,身份验证处理程序将反序列化它,并将其用作当前用户的标识。
    2. 如果未成功返回 ClaimsPrincipal,处理程序将存储结果,如果身份验证受到质询(例如,由于用户正在访问受保护的资源),则请求的响应将使用状态代码和来自身份验证终结点的响应中选择的响应标头进行更新。 这使质询响应(如重定向到登录页)可以传播给最终用户。
      1. 由于来自 ASP.NET 应用的身份验证终结点的结果可能包含特定于该终结点的数据,因此用户可以向 ASP.NET Core 应用注册 IRemoteAuthenticationResultProcessor 实现,该应用将在使用前在任何身份验证结果上运行。 例如,内置的 IRemoteAuthenticationResultProcessorRedirectUrlProcessor,它会查找从身份验证终结点返回的 Location 响应标头,并确保它们重定向回 ASP.NET Core 应用的主机,而不是直接重定向回到 ASP.NET 应用。

已知的限制

此远程身份验证方法有一些已知限制:

  1. Windows 身份验证:由于 Windows 身份验证依赖于 Windows 标识的句柄,因此此功能不支持 Windows 身份验证。 计划将来的工作探讨共享 Windows 身份验证的工作原理。 有关详细信息,请参阅 dotnet/systemweb-adapters#246 。 对于 Windows 身份验证方案,请考虑在您的 ASP.NET Core 应用程序中使用 配置 Windows 身份验证
  2. 用户管理作:此功能允许 ASP.NET Core 应用使用由 ASP.NET 应用进行身份验证的标识,但与用户相关的所有作(登录、注销等)仍需通过 ASP.NET 应用路由。
  3. 性能注意事项:每个身份验证请求都需要对 ASP.NET 应用的 HTTP 调用,这可能会影响性能。 如果性能至关重要,请考虑使用共享 cookie 身份验证(在“替代项”部分中所述)。

如果 ASP.NET 应用中的身份验证是使用 Microsoft.OwinCookie 身份验证中间件完成,则远程身份验证的替代解决方案是配置 ASP.NET 和 ASP.NET 核心应用,以便他们能够共享身份验证 cookie。

共享 Cookie 的工作原理

共享身份验证 cookie 可实现:

  • 这两个应用程序都能从同一个 cookie 确定用户身份。
  • 登录或注销一个应用可让用户登录或注销另一个应用。

这两个应用程序都配置为:

  • 使用相同的 cookie 名称和域设置
  • 共享用于加密/解密的 cookie 数据保护密钥
  • 使用兼容的 cookie 身份验证中间件

这样,一个应用中经过身份验证的用户可以在发出请求时自动在其他应用中进行身份验证。

优点 缺点
共享身份验证的最佳性能 仅适用于 OWIN cookie 身份验证
没有其他 HTTP 请求 需要匹配数据保护设置
这两个应用都可以处理登录/注销 更复杂的初始配置
无缝用户体验 仅限基于cookie的身份验证
降低网络开销 需要协调部署

共享 Cookie 身份验证的限制

请注意,由于登录通常依赖于特定数据库,并非所有身份验证功能都适用于这两个应用:

  • 用户应仅通过其中一个应用(ASP.NET 或 ASP.NET Core 应用)登录,无论数据库是设置使用哪个应用。
  • 这两个应用都可以查看用户的标识和声明。
  • 这两个应用都可以注销用户。

共享文档中提供了cookie有关如何在 ASP.NET 和 ASP.NET Core 应用之间配置共享身份验证 Cookie 的详细信息。 有关 ASP.NET Core 中的身份验证的详细信息cookie,请参阅在不使用 ASP.NET Core cookie的情况下使用Identity身份验证

System.Web 适配器 GitHub 存储库中的以下示例演示了使用共享配置实现用户登录和注销的远程cookie应用身份验证:

在以下情况下选择“共享 Cookie 身份验证”

  • ASP.NET 应用已使用 Microsoft.Owincookie 身份验证
  • 可以在两个应用之间配置匹配的数据保护设置
  • 性能至关重要,希望最大程度地减少 HTTP 请求
  • 你希望这两个应用处理用户登录/注销操作
  • 你可以轻松地管理共享加密密钥

如果两者均为 true,则共享身份验证是一个不错的选择:

  • ASP.NET 应用已在使用 Microsoft.Owincookie 身份验证。
  • 可以更新 ASP.NET 应用和 ASP.NET 核心应用以使用匹配的数据保护设置。 匹配共享数据保护设置包括用于存储数据保护密钥的共享文件路径、Redis 缓存或 Azure Blob 存储。 有关详细信息,请参阅 ASP.NET Core 数据保护概述

对于其他方案,本文档前面所述的远程身份验证方法更灵活,可能更合适。

另请参阅