上个月,我们推出了预览工具,以逐步将ASP.NET应用程序迁移到ASP.NET Core。该工具由两个部分组成:一个是Visual Studio扩展,在现有的ASP.NET应用旁边创建一个新的ASP.NET Core应用,这样端点就可以逐渐从一个应用转移到另一个应用;另一个是System.Web适配器API库,允许代码引用常见的System.Web API,同时以.NET标准2.0为目标,可以在ASP.NET和ASP.NET Core环境下使用。
我们最近发布了这个ASP.NET迁移工具的预览版2。更新后的工具包括对Visual Studio扩展生成的代码的改进,适配器库中额外的System.Web表面区域,以及在ASP.NET和ASP.NET Core应用程序之间共享认证的能力,这样,即使用户的浏览体验涉及新旧应用程序提供的端点,也只需登录一次。
作为提醒,System.Web适配器的开发正在GitHub上进行。在该资源库中,你可以找到文档,浏览最新的源代码,或者通过创建问题或拉动请求来贡献。
开始使用
如果这是你第一次测试增量ASP.NET迁移工具,你需要安装Microsoft Project MigrationsVisual Studio扩展。
当你使用该扩展来迁移ASP.NET项目时,它会自动添加对System.Web适配器预览2版本的引用。但如果你需要直接引用System.Web适配器(例如在类库中使用它们),可以从NuGet安装:<PackageReference Include="Microsoft.AspNetCore.SystemWebAdapters" Version="1.0.0-preview.2.22316.1" /> 。
升级迁移工具
如果你以前测试过增量ASP.NET迁移工具的预览版1,你可以直接从Visual Studio中更新Microsoft Project Migrations Visual Studio扩展。只要点击扩展菜单中的 "管理扩展 "命令。在那里,进入更新标签,寻找 "Microsoft Project Migrations "并点击更新按钮。

要更新对System.Web适配器的现有引用,只需更新到NuGet包的预览2版本:1.0.0-preview.2.22316.1 。
共享认证
上一篇博文解释了增量迁移和System.Web适配器的总体工作原理。在预览版2中,我们为正在迁移到ASP.NET Core的应用程序增加了在新旧项目之间共享认证的功能。这是通过将认证决定推迟到原始的ASP.NET应用程序来实现的。当应用程序选择共享认证时,一个认证处理程序被注册到ASP.NET Core应用程序中,该程序通过向ASP.NET应用程序发出HTTP请求来认证用户,其中包括与认证相关的头信息(默认为Authorization 和Cookie 头信息,但这是可以配置的)。ASP.NET应用程序中的一个自定义HTTP处理程序将为该请求提供服务,并根据请求中的头信息为被认证的用户返回一个序列化的索赔委托。
如果ASP.NET应用程序无法验证一个用户(也许还没有人登录),就不会返回任何索赔委托书。相反,ASP.NET应用程序将返回HTTP状态代码和与认证相关的响应头(默认为Location 、Set-Cookie 、WWW-Authenticate ),如果用户直接试图访问ASP.NET应用程序中需要授权的端点,它就会返回这些响应头。这些值存储在ASP.NET Core应用程序中,如果认证受到挑战(例如,因为用户正在访问受保护的端点),它们将被用来向用户返回HTTP响应,该响应与直接从ASP.NET应用程序收到的挑战响应类似。
因为在挑战的情况下,来自ASP.NET应用程序的响应可能与来自ASP.NET核心应用程序的响应不完全一致(例如,重定向位置需要固定,以引用正确的主机和原始路径),在将ASP.NET应用程序的响应传递给终端用户之前,会对其进行一些处理。System.Web适配器已经修复了与主机和重定向路径有关的已知问题,但如果你的特定认证方案需要对来自ASP.NET应用程序的响应进行额外的修改,你可以实现Microsoft.AspNetCore.SystemWebAdapters.Authentication.IRemoteAppAuthenticationResultProcessor 接口,并在ASP.NET Core应用程序的依赖注入容器中注册该实现。

已知的限制
远程应用程序认证适用于不记名令牌和基于cookie的认证方案。它目前不能与Windows认证一起使用,已知WS-Federation认证存在一些问题,正在调查中。
还需要注意的是,在ASP.NET身份认证方案中从ASP.NET核心应用中注销是不可行的,因为在这些方案中注销是通过向ASP.NET应用发出POST请求来完成的,由于防伪令牌验证在两个应用之间不工作,所以目前是不允许的。作为一个临时的解决方法,应用程序可以让注销操作将用户重定向到ASP.NET应用程序托管的页面,并让用户从那里注销。
演练
让我们通过一个例子来了解如何在增量的ASP.NET迁移方案中开始使用共享认证。
创建初始应用程序
首先,创建一个新的ASP.NET MVC应用程序,并选择 "个人账户 "进行认证。这将为我们提供一个包括ASP.NET身份认证的迁移起点。

启动该应用程序以确保其工作,并注册一个用户。
创建迁移项目
现在,在Visual Studio的解决方案资源管理器中右击该项目,选择 "迁移项目",使用项目迁移扩展来设置一个新的ASP.NET Core应用程序,它将与这个应用程序一起运行和部署,并允许我们逐步将端点迁移过来。请确保选择一个MVC模板,因为最初的应用程序是MVC。

新项目已经有了对Microsoft.AspNetCore.SystemWebAdapters 的引用,但我们需要在原来的ASP.NET应用程序中添加对该包的引用,以便它能在ASP.NET端配置远程应用程序端点。右键单击原始应用程序的引用文件夹,选择 "管理NuGet包 "并添加对Microsoft.AspNetCore.SystemWebAdapters 包的引用。
配置远程应用程序的认证
在这一点上,我们已经创建了新的ASP.NET Core应用程序,并将它不能提供的请求代理给原ASP.NET应用程序。我们现在需要配置远程应用程序认证,以便在两个应用程序之间共享用户身份。
在ASP.NET Core应用程序的Program.cs文件中,在现有的AddSystemWebAdapters 调用之后添加一个对AddRemoteAppAuthentication 的调用,如这里所示:
builder.Services.AddSystemWebAdapters()
.AddRemoteAppAuthentication(true, options =>
{
options.RemoteServiceOptions.RemoteAppUrl =
new(builder.Configuration["ReverseProxy:Clusters:fallbackCluster:Destinations:fallbackApp:Address"]);
options.RemoteServiceOptions.ApiKey = "test-key";
});
这将注册远程应用程序的认证处理程序。
传递给AddRemoteAppAuthentication 的第一个参数是一个布尔值,表示远程应用认证是否应该成为这个应用的默认认证方案。如果你传递的是true,那么在向任何没有指定不同认证方案的端点发出请求时,远程应用程序将默认查询用户的身份。这是全局启用远程应用程序认证的最简单的方法,但有一个缺点,即无论你是否需要使用用户的身份,都要对每个端点进行额外的HTTP请求。如果你为这个参数传递false,那么远程应用认证处理程序将被注册,但只用于特别请求远程应用认证方案的端点(通过在授权属性中指定方案,例如:[Authorize(AuthenticationSchemes = RemoteAppAuthenticationDefaults.AuthenticationScheme)] )。
传递给AddRemoteAppAuthentication 的第二个参数是用于配置RemoteAppAuthenticationOptions 的方法。在这个选项对象上必须设置的两个属性是:RemoteAppUrl ,这是认证请求应该被发送到的基本URL(我们可以从增量迁移工具已经设置的YARP配置中检索到,如上面的例子所示)和一个ApiKey 字符串,用于将ASP.NET Core应用认证为允许用远程应用认证用户的调用者。ApiKey 字符串可以是ASP.NET和ASP.NET Core应用程序之间安全共享的任何字符串。在本教程中,我们使用的是一个硬编码的字符串,但在现实世界的场景中,这应该是从应用程序的环境中安全加载的,甚至更好的是,像Azure Key Vault这样的安全存储。
我们还需要为ASP.NET Core应用程序启用认证中间件,因此在现有的对UseRouting 的调用之后,但在对UseAuthorization 的调用之前添加对app.UseAuthentication() 的调用。
现在,ASP.NET Core应用程序被设置为查询ASP.NET应用程序的认证,我们需要配置ASP.NET应用程序,使其能够响应这些请求。导航到ASP.NET应用程序的global.asax.cs文件,在Application_Start 方法的末尾添加以下调用:
Application.AddSystemWebAdapters()
.AddRemoteAppAuthentication(options =>
options.RemoteServiceOptions.ApiKey = "test-key");
你还需要在global.asax.cs的顶部添加一个导入语句using Microsoft.AspNetCore.SystemWebAdapters; 。这段代码看起来应该很熟悉。与在ASP.NET Core应用程序中注册认证处理程序的代码类似,这个调用是在ASP.NET应用程序中配置一个HTTP处理程序来处理认证请求。这里唯一需要的选项是ApiKey 属性,当然,它必须与ASP.NET Core应用程序中使用的API密钥相匹配。
添加一个测试页面
在这一点上,远程应用程序认证的配置是这样的:ASP.NET Core应用程序将看到登录到ASP.NET应用程序的用户的身份,如果ASP.NET Core应用程序挑战认证,它应该重定向到ASP.NET应用程序的登录页面。不过,为了测试这一点,我们需要一个利用用户身份的ASP.NET Core端点。
首先,将ASP.NET Core应用程序的Program.cs中对app.MapControllers(); 的调用替换为对MapDefaultControllerRoute 的调用,这样我们就可以很容易地路由到ASP.NET Core控制器。
app.MapDefaultControllerRoute();
接下来,在ASP.NET Core应用程序中添加一个Controllers文件夹,并创建一个简单的控制器,名为UserInfoController ,有一个Index动作方法:
using Microsoft.AspNetCore.Mvc;
namespace MigrationDemoCore.Controllers;
public class UserInfoController : Controller
{
[Authorize]
public IActionResult Index()
{
return View();
}
}
最后为控制器的索引端点创建一个视图,行使User.Identity 。在项目中创建一个名为Views的文件夹。在这个文件夹中,创建一个名为UserInfo的文件夹。最后创建一个名为Index.cshtml的视图,内容与此类似:
<h1>User Info</h1>
<h2>@(User.Identity?.Name??"Anonymous user")</h2>
<h3>Claims</h3>
<ul>
@foreach(var c in User.Claims)
{
<li>@c.Type: @c.Value</li>
}
</ul>
有了这些,我们就可以启动示例应用程序,看到它从头到尾都在工作。该应用程序的大部分端点仍然由原来的ASP.NET应用程序提供服务。但如果你导航到/UserInfo 路由,该端点就由ASP.NET Core应用程序提供服务。因为该端点受到[Authorize] 属性的保护,所以当未认证的用户试图访问该端点时,他们将被重定向到该应用的登录页面。对于已认证的用户,尽管/UserInfo 是由ASP.NET Core应用程序提供的,你可以看到用户的身份(由ASP.NET应用程序提供)是可用的:

请注意,即使你从/UserInfo 删除了[Authorize] 属性,如果有用户登录,端点仍然可以访问用户的身份。如果没有经过认证的用户,User.Identity 将为空。
总结
在这个增量ASP.NET迁移工具的第二个预览版中,增加了许多新的功能--最值得一提的是在增量升级方案中,在ASP.NET和ASP.NET Core应用程序之间共享认证的能力。使用这一功能,开发人员应该能够逐步将端点从现有的ASP.NET应用程序转移到新的ASP.NET Core应用程序中,即使这些端点需要对用户进行认证。
目前的共享认证解决方案是一个通用的解决方案,应该适用于大多数类型的认证,但我们正在继续研究其他的远程认证选项,这些选项可能对特定类型的认证更加有效或高效。我们还在探索共享认证,通过集中所有的认证决定,由ASP.NET核心应用程序(而不是ASP.NET应用程序)来实现。将该功能与Preview 2中已有的功能相结合,可以让开发者立即开始将端点转移到ASP.NET Core,但在迁移过程中的某个时间点将认证责任转移到ASP.NET Core应用。
除了共享认证工作外,我们还在继续扩大System.Web适配器的面积,以包括System.Web中更多最常用的API。
如果你对该工具有任何反馈,无论是错误报告还是功能建议,你都可以在GitHub上通过创建问题或拉动请求来为该项目做出贡献。