c# - 适用于 Windows-Identity 的 Asp.Net Core Intranet 应用程序中的模拟中间件

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

在解释我的问题之前,先介绍一下我们的场景:

场景

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

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

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

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

问题

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

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

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

https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1#impersonation enter image description here

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

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

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

问题

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

问题#2

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

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

问题

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

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

所以你可以使用RunImpersonatedAsync为了使其 super 明确,或者继续使用 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 Core 文档在这方面已经过时了,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 身份验证,这也使身份验证变得更加复杂。用户必须连接到域 Controller 才能从 NTLM 中受益,否则他们每次都必须使用其帐户数据对自己进行身份验证。您将无法切换到其他身份验证方式,例如使用 Azure AD 或 ADFS,这意味着您也不会从单点登录等功能中受益。

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

关于c# - 适用于 Windows-Identity 的 Asp.Net Core Intranet 应用程序中的模拟中间件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65407501/

相关文章:

c# - .NET Core 中的 SignedCms 替代方案

c# - MVC 操作未在 Controller 中触发

c# - EF ObjectStateEntry OriginalValues 包含 CurrentValues

c# - asp.net mvc 中的 LINQ + EntityFunctions

c# - Asp.Net Core v1.1 从同一解决方案中的另一个项目获取继承的类名

c# - 所有子 Crystal 报表绑定(bind)相同的数据

c# - MVC - 将发布数据插入数据库

asp.net-core - Aspnetcore 身份验证关联失败

asp.net-core - Blazor:每隔一段时间轮询服务器的对象?

c# - 相当于 Promise.then() 的任务是什么?