OWIN 集成

Microsoft.AspNetCore.SystemWebAdapters.Owin 库使 ASP.NET 核心应用程序能够在从 ASP.NET Framework 迁移期间使用 OWIN 中间件。 迁移依赖于基于 OWIN 的中间件的应用程序(尤其是身份验证、授权或自定义管道组件)时,此集成非常有用。

警告

使用 OWIN 集成时,可能需要禁止显示 .NET Framework 包依赖项的生成警告。 将以下内容添加到项目文件:

<PropertyGroup>
  <NoWarn>$(NoWarn);NU1701</NoWarn>
</PropertyGroup>

只要使用的 API 存在并且具有相同的行为,就支持在 .NET Core 上运行 .NET Framework 代码。 建议将此用作迁移过程的一部分,但不要长时间处于此状态。

如果使用,某些包需要手动更新为受支持的版本。 这些包括:

  • EntityFramework 必须为 >= 6.5.1,才能在 .NET Core 上获得最佳支持

为何使用 OWIN 集成

OWIN 集成在迁移过程中提供了多项优势:

  • 保留现有中间件:在不立即重写的情况下从 ASP.NET Framework 应用程序中重复使用 OWIN 中间件
  • 逐步迁移:在维护应用程序功能的同时以增量方式迁移中间件组件
  • 身份验证连续性:在迁移期间维护身份验证状态并在 ASP.NET Framework 和 ASP.NET Core 应用程序之间共享 Cookie
  • 熟悉的模式:继续使用团队已了解的 OWIN API 和模式

集成模式

该库提供三种不同的集成模式,每个模式都适合不同的迁移方案:

  1. OWIN 管道作为主管道中间件:将 OWIN 中间件直接添加到 ASP.NET 核心中间件管道
  2. OWIN 管道作为身份验证处理程序:使用 OWIN 中间件作为 ASP.NET 核心身份验证处理程序
  3. 模拟 HttpApplication 事件的 OWIN 管道:在模拟 HttpApplication 管道中运行 OWIN 中间件

OWIN 管道作为中间件

此模式将 OWIN 中间件合并到 ASP.NET 核心中间件管道中,而无需使用模拟 HttpApplication 事件。

何时使用此模式

在以下情况下使用此方法:

  • 你需要标准的中间件管道行为
  • 应用程序不依赖于 HttpApplication 事件
  • 逐步将 OWIN 中间件迁移到 ASP.NET 核心模式

设置

将 OWIN 中间件添加到主管道:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.UseOwin(owinApp =>
{
    owinApp.UseMyOwinMiddleware();
});

app.UseRouting();
app.MapControllers();

app.Run();

访问服务

配置 OWIN 中间件时访问服务:

app.UseOwin((owinApp, services) =>
{
    var configuration = services.GetRequiredService<IConfiguration>();
    owinApp.UseMyOwinMiddleware(configuration);
});

OWIN 身份验证处理程序

此模式使用 OWIN 中间件作为 ASP.NET 核心身份验证处理程序,与 AuthenticationBuilder API 集成。

何时使用此模式

在以下情况下使用此方法:

  • 从 ASP.NET Framework 迁移身份验证逻辑
  • 在 ASP.NET Framework 和 ASP.NET Core 应用程序之间共享身份验证 Cookie
  • 使用 OWIN 身份验证中间件,例如 cookie 身份验证或 OAuth 提供程序
  • 使用 ASP.NET Framework Identity

设置

将 OWIN 身份验证注册为 ASP.NET 核心身份验证处理程序:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication()
    .AddOwinAuthentication((owinApp, services) =>
    {
        owinApp.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login")
        });
    });

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

// Additional middleware or endpoint mapping

app.Run();

自定义身份验证方案

指定自定义身份验证方案名称:

builder.Services
    .AddAuthentication()
    .AddOwinAuthentication("MyOwinScheme", (owinApp, services) =>
    {
        owinApp.UseMyOwinAuthenticationMiddleware();
    });

如果未提供方案名称,身份验证处理程序将用作 OwinAuthenticationDefaults.AuthenticationScheme 默认方案。 身份验证方案名称确定:

  • 默认身份验证:如果设置为默认方案,则无需属性 [Authorize] 即可自动对请求进行身份验证
  • 质询行为:控制哪个处理程序响应身份验证挑战(例如重定向到登录页面)
  • 身份验证选择:允许你显式地使用特定认证方案进行身份验证HttpContext.AuthenticateAsync("scheme-name")

对于具有多个身份验证方案的应用程序,可以配置默认身份验证和质询方案:

builder.Services
    .AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = "MyOwinScheme";
        options.DefaultChallengeScheme = "MyOwinScheme";
    })
    .AddOwinAuthentication("MyOwinScheme", (owinApp, services) =>
    {
        owinApp.UseMyOwinAuthenticationMiddleware();
    });

有关身份验证方案以及如何在 ASP.NET Core 中工作的详细信息,请参阅 ASP.NET 核心身份验证和使用cookie身份验证概述,而无需 ASP.NET 核心Identity

访问 OWIN 身份验证

继续使用 OWIN IAuthenticationManager 接口:

var authManager = HttpContext.GetOwinContext().Authentication;

authManager.SignIn(identity);
authManager.SignOut();

ClaimsPrincipal.Current

如果代码需要使用 ClaimsPrincipal.Current,请参阅 “从静态 ClaimsPrincipal 访问迁移 ”以获取启用该属性设置的选项。

迁移 ASP.NET 框架 Identity

常见的迁移方案涉及 ASP.NET 框架Identity与 OWIN cookie身份验证。 此示例演示如何在迁移期间保持兼容性。

配置数据保护

配置数据保护,以便与 ASP.NET 框架设置 cookie 的共享相匹配:

var builder = WebApplication.CreateBuilder(args);

var sharedApplicationName = "CommonMvcAppName";
builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(Path.Combine(Path.GetTempPath(), "sharedkeys", sharedApplicationName)))
    .SetApplicationName(sharedApplicationName);

配置 OWIN 身份验证

使用 Identity 服务设置 OWIN 身份验证:

builder.Services
    .AddAuthentication()
    .AddOwinAuthentication("AppAuthenticationScheme", (app, services) =>
    {
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

        var dataProtector = services.GetDataProtector(
            "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware",
            "AppAuthenticationScheme",
            "v2");

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
            },
            CookieName = ".AspNet.ApplicationCookie",
            TicketDataFormat = new AspNetTicketDataFormat(new DataProtectorShim(dataProtector))
        });
    });

在控制器中使用

在控制器中访问 OWIN 注册的服务:

using Microsoft.AspNet.Identity.Owin;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SystemWebAdapters;
using Microsoft.Owin.Security;

[Authorize]
public class AccountController : Controller
{
    public ApplicationSignInManager SignInManager =>
        HttpContext.GetOwinContext().Get<ApplicationSignInManager>();

    public ApplicationUserManager UserManager =>
        HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();

    private IAuthenticationManager AuthenticationManager =>
        HttpContext.GetOwinContext().Authentication;

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(LoginViewModel model)
    {
        var result = await SignInManager.PasswordSignInAsync(
            model.Email, 
            model.Password, 
            model.RememberMe, 
            shouldLockout: false);

        if (result == SignInStatus.Success)
        {
            return RedirectToAction("Index", "Home");
        }

        ModelState.AddModelError("", "Invalid login attempt.");
        return View(model);
    }

    [HttpPost]
    [ValidateAntiForgeryToken]
    public IActionResult Logout()
    {
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
        return RedirectToAction("Index", "Home");
    }
}

关键配置点

迁移 ASP.NET 框架 Identity时:

  • 数据保护:在 ASP.NET Framework 和 ASP.NET Core 中配置相同 ApplicationName 和密钥存储位置
  • 按请求服务:用于 app.CreatePerOwinContext<T>() 注册每个请求创建一次的服务
  • 数据保护网桥:使用AspNetTicketDataFormatDataProtectorShim将 ASP.NET Core 的IDataProtector桥接到 OWIN
  • 服务访问:通过 HttpContext.GetOwinContext() 访问 OWIN 注册的服务
  • Cookie 名称:确保 CookieName 共享身份验证状态时应用程序之间的匹配项

HttpApplication 事件中的 OWIN 管道

此模式在模拟 HttpApplication 事件管道中运行 OWIN 中间件,类似于 OWIN 在 ASP.NET Framework 的集成管道模式下的工作方式。

何时使用此模式

在以下情况下使用此方法:

  • 应用程序依赖于 ASP.NET Framework 集成管道
  • OWIN 中间件需要在特定 HttpApplication 事件阶段执行
  • 你正在迁移使用HttpApplication事件和OWIN的应用程序

设置

HttpApplication 事件中配置运行 OWIN 管道:

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddSystemWebAdapters()
    .AddOwinApp(app =>
    {
        app.UseMyOwinMiddleware();
        app.UseStageMarker(PipelineStage.Authenticate);
    });

var app = builder.Build();

app.UseSystemWebAdapters();
app.Run();

访问服务

在配置 OWIN 管道时访问 IServiceProvider

builder.Services
    .AddSystemWebAdapters()
    .AddOwinApp((app, services) =>
    {
        var configuration = services.GetRequiredService<IConfiguration>();
        app.UseMyOwinMiddleware(configuration);
        app.UseStageMarker(PipelineStage.Authenticate);
    });

HttpApplication 事件映射

OWIN 管道与以下 HttpApplication 事件集成:

  • AuthenticateRequest / PostAuthenticateRequest
  • AuthorizeRequest / PostAuthorizeRequest
  • ResolveRequestCache / PostResolveRequestCache
  • MapRequestHandler / PostMapRequestHandler
  • AcquireRequestState / PostAcquireRequestState
  • PreRequestHandlerExecute

使用 .UseStageMarker(PipelineStage) 控制 OWIN 中间件相对于这些事件的执行顺序。

迁移策略

将 OWIN 中间件合并到 ASP.NET Core 应用程序中时:

  1. 确定 OWIN 依赖项:确定应用程序使用的 OWIN 中间件
  2. 选择集成模式:根据应用程序的需求选择适当的模式
  3. 配置数据保护:为身份验证 cookie 共享设置共享数据保护
  4. 测试身份验证流:验证身份验证在 ASP.NET Core 应用程序中正常工作
  5. 逐步转换:计划随着时间的推移将 OWIN 中间件迁移到本机 ASP.NET 核心中间件
  6. 监视兼容性:确保 OWIN 中间件行为在迁移过程中符合预期

其他资源