Asp.Net 核心 Intranet 应用程序中用于 Windows-Identity 的模拟中间件

Impersonation Middleware in an Asp.Net Core Intranet app for Windows-Identity

在我解释我的问题之前,这是我们的场景:

场景

我们仅为内部网 Windows 用户编写软件(目前由本地 Active Directory 管理,但将来我们可能会迁移到 Azure-AD)。

到目前为止,还有一个旧的单体 Winforms 应用程序,它使用数据集直接与数据库通信。所有对数据库的请求都发生在 WindowsIdentity(最终用户上下文)中,因此数据库知道最终用户。

为了将来的发展,我们希望使用 Web API 来处理业务逻辑。只有 web 应用程序应该使用 Entity Framework 访问数据库。我们编写了一个 Web API 和 ASP.NET 核心(无状态),托管在 IIS 中。因为网络应用程序 运行 在应用程序池身份中,我们编写了一个中间件来模拟最终用户的上下文(以便数据库访问正常工作)。

迁移到 Web 服务器时,必须支持两个版本,因为我们无法一次迁移整个应用程序。

问题

在调试环境中,网络服务器工作正常(因为没有模拟),但在实时系统中,服务器有时会崩溃(不是每次)。

我们遇到的最常见错误是 FileLoadException,它无法加载 dll(主要是 System.Reflection)。其他时候整个服务器 运行s 没有错误 return 200 OK 但不包含任何 http 主体。

看来是有问题了。文档对此有一个小提示:

https://docs.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1#impersonation

所以 运行 用户上下文中的整个请求似乎是个坏主意,但可能是解决方案吗?

当然,我们要丢弃的只是模拟中间件。但是怎么访问数据库呢?

选项 #1:运行 模拟每个数据库调用

问题

官方文档说模拟上下文中的代码禁止 运行 异步。我们可以做到,没问题。但是我不知道 Entity Framework 运行 的异步部分是不是?如果发生这种情况,我们的应用程序可能会再次崩溃。你有什么想法吗?

问题 #2

如果有两个请求进入 asp 将 运行 两个线程。是否允许在某个时间对两个请求异步调用模拟?官方文档不是很清楚

选项 #2:更改我们的数据库,因此不需要最终用户

问题

一些 SQL 触发器(和存储的程序)使用用户名,例如在插入时将名称写入 table。当然,我们可以更改该行为,以便 EF Core 手写名称。

这个问题更像是一个政治问题,目的是找出我们应该将工作解决方案从 WindowsIdentity 更改为 AppIdentity 的原因。你有什么好的论据吗?

选项#3:你还有什么想法吗?

我没有看到更多的解决方案,也许你有?


顺便说一句。这是我们模拟中间件的代码:

public class ImpersonateMiddleware
{
    private readonly RequestDelegate _next;

    public ImpersonateMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        var winIdent = context?.User?.Identity as WindowsIdentity;

        if (winIdent == null)
        {
            await _next.Invoke(context).ConfigureAwait(true);
        }
        else
        {
            await WindowsIdentity.RunImpersonated(winIdent.AccessToken, async () =>
            {
                await _next.Invoke(context)
                    .ConfigureAwait(true) 
                    ;
            }).ConfigureAwait(true);
        }
    }
}

1.1 Question: The official documentation says that code in the impersonation context is forbidden to run async things. We can do that, no problem. But I don't know if the EntityFramework runs async parts? If that would happen our app can crash again. Have you any idea?

我实际上认为这部分文档已经过时 and/or 错误。之前有人请求 RunImpersonated 进行异步重载,the issue 提到传递异步函数可以正常工作,因为模拟将在内部使用异步本地。

所以你可以使用 RunImpersonatedAsync for this to be super explicit, or just keep using RunImpersonated 因为这实际上是在做同样的事情。文档也明确允许将其用于异步工作:

This method may be used reliably with the async/await pattern, unlike Impersonate. In an async method, the generic overload of this method may be used with an async delegate argument so that the resulting task may be awaited.

我认为 ASP.NET 核心文档在此方面已经过时,opened an issue for this 需要更正或澄清。

运行 模拟的整个请求也应该可以正常工作,所以除非我在那个问题上听到其他消息,否则我认为您在应用程序中看到的崩溃可能有不同的原因,而不仅仅是模拟.我建议您首先尝试使用不同的(非开发)环境使用和不使用模拟分别重现它,看看这是否真的与模拟有关。 FileLoadException 至少听起来不像模拟的问题,除了可能模拟的用户无法访问某些延迟加载的程序集(我不确定模拟是否会在这里产生影响)。


1.2 Question: If two requests come in asp would run two threads. Is it allowed to call impersonation at some time async on both requests? The official documentation is not so clear.

由于模拟是使用异步本地实现的,因此模拟仅限于单个本地调用流程。因此,使用不同异步流的不同请求不会受到另一个被模拟的影响。这也使模拟线程安全。


2.1 Question: Some SQL-Trigger (and StoredProcedures) use the user name for e.g. write the name into a table on insert. Of course we can change that behaviour so the EFCore write the name by hand.

是的,将该用户名传递到您的查询中可能是可行的方法。

This problem is more a political one to find reasons why we should change the working solution away from WindowsIdentity to an AppIdentity. Have you some arguments?

我过去曾参与过一个也有此要求的项目,我可以说模仿数据库并没有真正说服我。为了使其工作,每个用户都需要被授权在数据库中进行更改。这也意味着,通过直接数据库访问,用户可能会做出他们不允许仅使用该应用程序进行的更改。他们基本上可以绕过任何特定于应用程序的授权和保护。

当然,用户不应该直接连接到数据库,但这总是会发生。如果没有适当的保护措施,这就变得更加关键了。还要考虑那些对您的机器具有管理员访问权限的人,例如正在开发应用程序的人。他们没有通过没有可以在数据库中进行更改的帐户来限制访问权限,而是默认情况下具有访问权限,因为这就是应用程序的工作方式。

与只有少数用户知道且访问受限的帐户数量有限相比,在这些情况下保护您的数据库要困难得多。

此外,由于您提到迁移到 Azure AD,为了让您的应用程序能够模拟,您的 AD 需要完全联合并且应用程序服务器需要是域的一部分。所以你必须牢记这一点。

而且,由于您仍然依赖 Windows 身份验证,这也使身份验证变得更加复杂。用户必须连接到域控制器才能从 NTLM 中受益,否则他们每次都必须使用他们的帐户数据对自己进行身份验证。您将无法切换到其他身份验证方式,例如使用 Azure AD 或 ADFS,这意味着您也不会从单点登录等方面受益。

(注意:您 可以 使用 Windows 身份验证以外的其他方式,并让应用程序通过创建 Windows 身份本身。但是相信我,你不想那样做;这是配置的噩梦)