【问题标题】:Impersonation Middleware in an Asp.Net Core Intranet app for Windows-Identity用于 Windows-Identity 的 Asp.Net Core Intranet 应用程序中的模拟中间件
【发布时间】:2021-04-01 02:04:23
【问题描述】:

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

场景

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

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

对于未来的开发,我们希望将 Web API 用于业务逻辑。只有网络应用程序应该使用实体框架访问数据库。我们使用托管在 IIS 中的 ASP.NET Core(无状态)编写了一个 Web API。由于 Web 应用程序以应用程序池身份运行,因此我们编写了一个中间件来模拟最终用户的上下文(以便数据库访问正常工作)。

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

问题

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

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

所以看起来有问题。该文档对此有一个小提示:

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

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

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

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

问题

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

问题 #2

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

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

问题

一些 SQL 触发器(和存储的 orocedures)使用用户名,例如在插入时将名称写入表中。当然,我们可以更改该行为,以便 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);
        }
    }
}

【问题讨论】:

    标签: c# entity-framework asp.net-core impersonation windows-identity


    【解决方案1】:

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

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

    因此,您可以使用 RunImpersonatedAsync 来表示超级明确,或者继续使用 RunImpersonated,因为这实际上是在做同样的事情。文档也明确允许将其用于异步工作:

    Impersonate 不同,此方法可以可靠地与 async/await 模式一起使用。在异步方法中,此方法的泛型重载可以与异步委托参数一起使用,以便可以等待生成的任务。

    我相信 ASP.NET Core 文档在这方面已经过时了,opened an issue for this 需要对其进行更正或澄清。

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


    1.2 问题:如果有两个请求进来,asp 会运行两个线程。是否允许在两个请求的某个时间异步调用模拟?官方文档不是很清楚。

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


    2.1 问题: 一些 SQL 触发器(和 StoredProcedures)使用用户名,例如在插入时将名称写入表中。当然,我们可以更改该行为,以便 EFCore 手写名称。

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

    这个问题更多地是一个政治问题,用于寻找我们应该将工作解决方案从 WindowsIdentity 更改为 AppIdentity 的原因。你有什么争论吗?

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

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

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

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

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

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

    【讨论】:

    • 哇,好详细的答案。非常感谢这个伟大的答案和你的努力。顺便说一句,FileLoadException 包含“AccessDenied”的原因。
    猜你喜欢
    • 2016-10-03
    • 2017-11-01
    • 2017-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多