C# HttpClient & Windows Auth:将登录的消费者用户传递给服务

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19316172/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 14:43:37  来源:igfitidea点击:

HttpClient & Windows Auth: Pass logged in User of Consumer to Service

c#asp.net-mvcweb-servicesauthenticationasp.net-web-api

提问by James

I am struggling to understand and set up a Service and Consumer where the Service will run as the user logged into the Consumer.

我正在努力理解和设置一个服务和消费者,该服务将在用户登录消费者时运行。

My consumer is an MVC application. My Service is a Web Api application. Both run on separate servers within the same domain. Both are set to use Windows Auth.

我的消费者是一个 MVC 应用程序。我的服务是一个 Web Api 应用程序。两者都在同一个域内的不同服务器上运行。两者都设置为使用 Windows 身份验证。

My consumer code is:

我的消费者代码是:

private T GenericGet<T>(string p)
    {
        T result = default(T);

        HttpClientHandler handler = new HttpClientHandler() { PreAuthenticate = true, UseDefaultCredentials = true };
        using (HttpClient client = new HttpClient(handler))
        {
            client.BaseAddress = new Uri(serviceEndPoint);

            HttpResponseMessage response = client.GetAsync(p).Result;
            if (response.IsSuccessStatusCode)
                result = response.Content.ReadAsAsync<T>().Result;
        }

        return result;
    }

In my Service I call User.Identity.Nameto get the caller ID but this always comes back as the consumer App Pool ID, not the logged in user. The consumer App Pool is running as a Network Service, the server itself is trusted for delegation. So how do I get the logged in User? Service code:

在我的服务中,我打电话User.Identity.Name来获取来电显示,但这总是作为消费者应用程序池 ID 返回,而不是登录用户。消费者应用程序池作为网络服务运行,服务器本身受委托进行委托。那么如何获取登录用户呢?服务代码:

    // GET: /Modules/5/Permissions/
    [Authorize]
    public ModulePermissionsDTO Get(int ModuleID)
    {
        Module module= moduleRepository.Find(ModuleID);
        if (module== null)
            throw new HttpResponseException(HttpStatusCode.NotFound);

        // This just shows as the App Pool the MVC consumer is running as (Network Service).
        IPrincipal loggedInUser = User;

        // Do I need to do something with this instead?
        string authHeader = HttpContext.Current.Request.Headers["Authorization"];

        ModulePermissionsDTO dto = new ModulePermissionsDTO();
        // Construct object here based on User...

        return dto;
    }

According to this question, Kerberos is required to make this set up work because the HttpClient runs in a separate thread. However this confuses me because I thought the request sends an Authorization header and so the service should be able to use this and retrieve the user token. Anyway, I have done some testing with Kerberos to check that this correctly works on my domain using the demo in "Situation 5" hereand this works but my two applications still wont correctly pass the logged in user across.

根据这个问题,由于 HttpClient 在单独的线程中运行,因此需要 Kerberos 来进行此设置。然而,这让我感到困惑,因为我认为该请求发送了一个 Authorization 标头,因此该服务应该能够使用它并检索用户令牌。无论如何,我已经使用 Kerberos 进行了一些测试,以使用此处“情况 5”中的演示来检查这是否在我的域上正常工作,并且这有效,但我的两个应用程序仍然无法正确传递登录用户。

So what do I need to do to make this work? Is Kerberos needed or do I need to do something in my Service to unpack the Authorisation header and create a principal object from the token? All advice appreciated.

那么我需要做什么才能使这项工作发挥作用?是否需要 Kerberos 或者我是否需要在我的服务中做一些事情来解压 Authorization 标头并从令牌创建一个主体对象?所有建议表示赞赏。

采纳答案by Eiríkur Fannar Torfason

The key is to let your MVC application (consumer) impersonate the calling user and then issue the HTTP requests synchronously (i.e. without spawning a new thread). You should not have to concern yourself with low-level implementation details, such as NTLM vs Kerberos.

关键是让您的 MVC 应用程序(消费者)模拟调用用户,然后同步发出 HTTP 请求(即不产生新线程)。您不必担心低级实现细节,例如 NTLM 与 Kerberos。

Consumer

消费者

Configure your MVC application like so:

像这样配置您的 MVC 应用程序:

  1. Start IIS Manager
  2. Select your MVC web application
  3. Double click on 'Authentication'
  4. Enable'ASP.NET Impersonation'
  5. Enable'Windows Authentication'
  6. Disableother forms of authentication (unless perhaps Digest if you need it)
  7. Open the Web.config file in the root of your MVC application and ensure that <authentication mode="Windows" />
  1. 启动 IIS 管理器
  2. 选择您的 MVC Web 应用程序
  3. 双击“身份验证”
  4. 启用“ASP.NET 模拟”
  5. 启用“Windows 身份验证”
  6. 禁用其他形式的身份验证(除非可能需要 Digest)
  7. 打开 MVC 应用程序根目录中的 Web.config 文件并确保 <authentication mode="Windows" />

To issue the HTTP request, I recommend you use the excellent RestSharplibrary. Example:

要发出 HTTP 请求,我建议您使用优秀的RestSharp库。例子:

var client = new RestClient("<your base url here>");
client.Authenticator = new NtlmAuthenticator();
var request = new RestRequest("Modules/5/Permissions", Method.GET);
var response = client.Execute<ModulePermissionsDTO>(request);

Service

服务

Configure your Web API service like so:

像这样配置您的 Web API 服务:

  1. Start IIS Manager
  2. Select your Web API service
  3. Double click on 'Authentication'
  4. Disable'ASP.NET Impersonation'.
  5. Enable'Windows Authentication'
  6. If only a subset of your Web API methods requires users to be authenticated, leave 'Anonymous Authentication' enabled.
  7. Open the Web.config file in the root of your Web API service and ensure that <authentication mode="Windows" />
  1. 启动 IIS 管理器
  2. 选择您的 Web API 服务
  3. 双击“身份验证”
  4. 禁用“ASP.NET 模拟”。
  5. 启用“Windows 身份验证”
  6. 如果只有一部分 Web API 方法需要对用户进行身份验证,请启用“匿名身份验证”。
  7. 打开 Web API 服务根目录中的 Web.config 文件并确保 <authentication mode="Windows" />

I can see that you've already decorated your method with a [Authorize]attribute which should trigger an authentication challenge (HTTP 401) when the method is accessed. Now you should be able to access the identity of your end user through the User.Identityproperty of your ApiController class.

我可以看到您已经使用一个[Authorize]属性装饰了您的方法,该属性应该在访问该方法时触发身份验证质询 (HTTP 401)。现在您应该能够通过User.IdentityApiController 类的属性访问最终用户的身份。

回答by Rahul Garg

The key issue with double hop is delegation of user credential to second call. I want to elaborate a little bit about it. C1 = client browser , S1 = First Server , S2 = Second Server.

双跳的关键问题是将用户凭据委派给第二次调用。我想详细说明一下。C1 = 客户端浏览器,S1 = 第一服务器,S2 = 第二服务器。

Suppose our complete system support window authentication. When user access S1 from browser , its default window credential pass to server S1, but when S1 make a call to S2 , by default it don't pass credential to S2.

假设我们完整的系统支持窗口认证。当用户从 browser 访问 S1 时,它的默认窗口凭证会传递给服务器 S1,但是当 S1 调用 S2 时,默认情况下它不会将凭证传递给 S2。

Resolution :

解析度 :

  1. We must enable window authentication/ impersonation on both machines.
  2. WE need to enable delegation between server so that S1 can trust to S2 and will pass credential to S2.
  1. 我们必须在两台机器上启用窗口身份验证/模拟。
  2. 我们需要启用服务器之间的委托,以便 S1 可以信任 S2 并将凭据传递给 S2。

You can find some useful details at below links : http://blogs.msdn.com/b/farukcelik/archive/2008/01/02/how-to-set-up-a-kerberos-authentication-scenario-with-sql-server-linked-servers.aspx

您可以在以下链接中找到一些有用的详细信息:http://blogs.msdn.com/b/farukcelik/archive/2008/01/02/how-to-set-up-a-kerberos-authentication-scenario-with- sql-server-linked-servers.aspx

https://sqlbadboy.wordpress.com/2013/10/11/the-kerberos-double-hop-problem/

https://sqlbadboy.wordpress.com/2013/10/11/the-kerberos-double-hop-problem/

回答by sachin mhalungekar

If you are trying to access service which is hosted on windows authentication then do following.

如果您尝试访问托管在 Windows 身份验证上的服务,请执行以下操作。

var request = new RestRequest(Method.POST);

If you want to use applications default credentials which must have access on hosted service server

如果要使用必须在托管服务服务器上具有访问权限的应用程序默认凭据

request.UseDefaultCredentials = true;

oruser below to pass the credentials manually

下面的用户手动传递凭据

request.Credentials = new NetworkCredential("Username", "Password", "Domain");