HttpClient调用一个windows - authentication ApiController方法,但是没有W



是否有一种方法可以让我的api控制器获得在api控制器使用windows身份验证时发起调用api控制器的帐户的IIdentity ?

我"castController.User

。"身份"是(类型)WindowsIdentity。但它是"空的"。空,如:IsAuthenticated = false和空UserName。它不是空的,它是"empty"

我的"WebTier"是一个运行自定义appool的IIS应用程序,而运行自定义appool的IIdentity类似于"mydomainmyServiceAccount"。我试图获得"castController.User.Identity"。

将"Name"值指定为此服务帐户。

(我猜它可能是任何客户端谁能够连接到我的WebApiTier与一个有效的windows帐户,但我提到这只是以防它可能扔一个奇怪的扳手)

我的"WebTier"(Mvc应用程序)有这个方法:

你会注意到我使用UseDefaultCredentials的两种方式。(Aka,我一直试图弄清楚这一点)

    private async Task<HttpResponseMessage> ExecuteProxy(string url)
    {
            HttpClientHandler handler = new HttpClientHandler()
            {
                UseDefaultCredentials = true
            };
            handler.PreAuthenticate = true;
            WebRequestHandler webRequestHandler = new WebRequestHandler();
            webRequestHandler.UseDefaultCredentials = true;
            webRequestHandler.AllowPipelining = true;
            webRequestHandler.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequired;
            webRequestHandler.ImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Identification;

            using (var client = new HttpClient(handler)) /* i've tried webRequestHandler too */ 
            {
                Uri destinationUri = new Uri("http://localhost/MyVirtualDirectory/api/mycontroller/mymethod");
                this.Request.RequestUri = destinationUri;
                return await client.SendAsync(this.Request);
            }
    }

" WebApiTier "设置。

web . config

  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <authentication mode="Windows" />

"WebApiTier"代码

public MyController : ApiController
{
    [ActionName("MyMethod")]
    [MyCustomAuthorization]
    public IEnumerable<string> MyMethod()
    {
                return new string[] { "value1", "value2" };
    }
}

public class MyCustomAuthorizationAttribute : System.Web.Http.AuthorizeAttribute
{
    private string CurrentActionName { get; set; }

    public override void OnAuthorization(HttpActionContext actionContext)
    {
        this.CurrentActionName = actionContext.ActionDescriptor.ActionName;
        base.OnAuthorization(actionContext);
    }
    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        var test1 = System.Threading.Thread.CurrentPrincipal;
        /* the above is "empty" */
        ////string userName = actionContext.RequestContext.Principal;/*  Web API v2  */
        string userName = string.Empty;
        ApiController castController = actionContext.ControllerContext.Controller as ApiController;
        if (null != castController)
        {
            userName = castController.User.Identity.Name; 
            /* the above is "empty" */
        }
        return true;
    }
}

}

。我没有做"双跳"(我在一些地方读到过)。
两个层都在同一域中(和本地开发,它们在同一台机器上)....

有趣的是,我读过这个(如何让HttpClient与请求一起传递凭据?)报告的"问题"正是我想要的工作方式。(? ! ? !)。

对于开发,"WebApiTier"在完整的IIS下运行。对于"WebTier",我在IIS- express和完整的IIS下尝试过。

我还运行了一个控制台应用程序,代码如下:

控制台应用程序

    IEnumerable<string> returnItems = null;
        HttpClientHandler handler = new HttpClientHandler()
        {
            UseDefaultCredentials = true
        };
        handler.PreAuthenticate = true;

        WebRequestHandler webRequestHandler = new WebRequestHandler();
        webRequestHandler.UseDefaultCredentials = true;
        webRequestHandler.AllowPipelining = true;
        webRequestHandler.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequired;
        webRequestHandler.ImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Identification;

        HttpClient client = new HttpClient(handler);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    string serviceUrl = "http://localhost/MyVirtualDirectory/api/mycontroller/mymethod";
    HttpResponseMessage response = client.GetAsync(new Uri(serviceUrl)).Result;
    var temp1 = (response.ToString());
    var temp2 = (response.Content.ReadAsStringAsync().Result);
    if (response.IsSuccessStatusCode)
    {
        Task<IEnumerable<string>> wrap = response.Content.ReadAsAsync<IEnumerable<string>>();
        if (null != wrap)
        {
            returnItems = wrap.Result;
        }
        else
        {
            throw new ArgumentNullException("Task<IEnumerable<string>>.Result was null.  This was not expected.");
        }
    }
    else
    {
        throw new HttpRequestException(response.ReasonPhrase + " " + response.RequestMessage);
    }

与其他代码的结果相同。一个"空"的Windows标识符。

我也看了这个

http://www.iis.net/configreference/system.webserver/security/authentication/windowsauthentication

只是为了检查一下。

好的。我找到了问题所在。感谢这篇文章。

如何在身份冒充时获取Windows用户名="true"在asp.net ?

//开始引用//

在应用程序中使用<authentication mode="Windows"/>,并且在IIS中启用匿名访问,您将看到以下结果:

System.Environment.UserName: Computer Name
Page.User.Identity.Name: Blank
System.Security.Principal.WindowsIdentity.GetCurrent().Name: Computer Name

//

"

所以我也将包括一个完整的答案.......显示问题和一些可能需要调整的设置。

去下载这个小例子。

https://code.msdn.microsoft.com/ASP-NET-Web-API-Tutorial-8d2588b1

这将给你一个名为ProductsApp (ProductsApp.csproj)的快速"WebApiTier"。

如果你想自己做....只要创建一个WebApi控制器…返回一些product。

public class ProductsController : ApiController
{
    Product[] products = new Product[] 
    { 
        new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 }, 
        new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M }, 
        new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M } 
    };
    [IdentityWhiteListAuthorization]
    public IEnumerable<Product> GetAllProducts()
    {
        return products;
    }
}

打开上面的。sln.

添加一个新的"类库" csproj名为"WebApiIdentityPoc.Domain.csproj"。

在这个库中创建一个新类。

namespace WebApiIdentityPoc.Domain
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

删除(或注释掉)

ProductsApp 模型 Product.cs

在ProductsApp中添加一个(项目)引用到WebApiIdentityPoc.Domain。

修复

中的命名空间问题

ProductsApp 控制器 ProductsController.cs

//using ProductsApp.Models;
using WebApiIdentityPoc.Domain;
namespace ProductsApp.Controllers
{
    public class ProductsController : ApiController
    {

(你基本上是将"Product"对象移动到另一个库中,这样服务器和客户端就可以共享同一个对象。)

此时应该可以编译了。

..........

在解决方案中添加一个新的"Console Application"项目。

WebApiIdentityPoc.ConsoleOne.csproj

使用Nuget添加"Newtonsoft"。WebApiIdentityPoc.ConsoleOne.csproj.

添加引用(框架或扩展使用右键单击/添加引用在"/references"文件夹在csproj)

System.Net.Http
System.Net.Http.Formatting
System.Net.Http.WebRequest (this one is may not be needed)

为WebApiIdentityPoc.Domain添加一个项目引用。

在控制台App的"Program.cs"中,粘贴以下代码:.............

namespace WebApiIdentityPoc.ConsoleOne
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Security.Principal;
    using System.Text;
    using System.Threading.Tasks;
    using Newtonsoft.Json;
    using WebApiIdentityPoc.Domain;
    public class Program
    {
        private static readonly string WebApiExampleUrl = "http://localhost:47503/api/Products/GetAllProducts"; /* check ProductsApp.csproj properties, "Web" tab, "IIS Express" settings if there is an issue */
        public static void Main(string[] args)
        {
            try
            {
                System.Security.Principal.WindowsIdentity ident = System.Security.Principal.WindowsIdentity.GetCurrent();
                if (null != ident)
                {
                    Console.WriteLine("Will the Identity '{0}' Show up in IdentityWhiteListAuthorizationAttribute ???", ident.Name);
                }
                RunHttpClientExample();
                RunWebClientExample();
                RunWebClientWicExample();
            }
            catch (Exception ex)
            {
                System.Text.StringBuilder sb = new System.Text.StringBuilder();
                Exception exc = ex;
                while (null != exc)
                {
                    sb.Append(exc.GetType().Name + System.Environment.NewLine);
                    sb.Append(exc.Message + System.Environment.NewLine);
                    exc = exc.InnerException;
                }
                Console.WriteLine(sb.ToString());
            }
            Console.WriteLine("Press ENTER to exit");
            Console.ReadLine();
        }
        private static void RunWebClientExample()
        {
            /* some articles said that HttpClient could not pass over the credentials because of async operations, these were some "experiments" using the older WebClient.  Stick with HttpClient if you can */
            WebClient webClient = new WebClient();
            webClient.UseDefaultCredentials = true;
            string serviceUrl = WebApiExampleUrl;
            string json = webClient.DownloadString(serviceUrl);
            IEnumerable<Product> returnItems = JsonConvert.DeserializeObject<IEnumerable<Product>>(json);
            ShowProducts(returnItems);
        }
        private static void RunWebClientWicExample()
        {
            /* some articles said that HttpClient could not pass over the credentials because of async operations, these were some "experiments" using the older WebClient.  Stick with HttpClient if you can */
            System.Security.Principal.WindowsIdentity ident = System.Security.Principal.WindowsIdentity.GetCurrent();
            WindowsImpersonationContext wic = ident.Impersonate();
            try
            {
                WebClient webClient = new WebClient();
                webClient.UseDefaultCredentials = true;
                string serviceUrl = WebApiExampleUrl;
                string json = webClient.DownloadString(serviceUrl);
                IEnumerable<Product> returnItems = JsonConvert.DeserializeObject<IEnumerable<Product>>(json);
                ShowProducts(returnItems);
            }
            finally
            {
                wic.Undo();
            }
        }
        private static void RunHttpClientExample()
        {
            IEnumerable<Product> returnItems = null;
            HttpClientHandler handler = new HttpClientHandler()
            {
                UseDefaultCredentials = true, PreAuthenticate = true
            };
            ////////WebRequestHandler webRequestHandler = new WebRequestHandler();
            ////////webRequestHandler.UseDefaultCredentials = true;
            ////////webRequestHandler.AllowPipelining = true;
            ////////webRequestHandler.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequired;
            ////////webRequestHandler.ImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Identification;
            using (HttpClient client = new HttpClient(handler))
            {
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                string serviceUrl = WebApiExampleUrl;
                HttpResponseMessage response = client.GetAsync(new Uri(serviceUrl)).Result;
                var temp1 = response.ToString();
                var temp2 = response.Content.ReadAsStringAsync().Result;
                if (response.IsSuccessStatusCode)
                {
                    Task<IEnumerable<Product>> wrap = response.Content.ReadAsAsync<IEnumerable<Product>>();
                    if (null != wrap)
                    {
                        returnItems = wrap.Result;
                    }
                    else
                    {
                        throw new ArgumentNullException("Task<IEnumerable<Product>>.Result was null.  This was not expected.");
                    }
                }
                else
                {
                    throw new HttpRequestException(response.ReasonPhrase + " " + response.RequestMessage);
                }
            }
            ShowProducts(returnItems);
        }
        private static void ShowProducts(IEnumerable<Product> prods)
        {
            if (null != prods)
            {
                foreach (Product p in prods)
                {
                    Console.WriteLine("{0}, {1}, {2}, {3}", p.Id, p.Name, p.Price, p.Category);
                }
                Console.WriteLine(string.Empty);
            }
        }
    }
}

你应该能够编译和运行,并看到一些产品显示在控制台应用程序。

…在"ProductsApp

/WebApiExtensions/

在这个文件夹下,添加一个新文件:

IdentityWhiteListAuthorizationAttribute.cs

粘贴到下面的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
namespace ProductsApp.WebApiExtensions
{
    public class IdentityWhiteListAuthorizationAttribute : System.Web.Http.AuthorizeAttribute
    {
        public IdentityWhiteListAuthorizationAttribute()
        {
        }
        private string CurrentActionName { get; set; }
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            this.CurrentActionName = actionContext.ActionDescriptor.ActionName;
            base.OnAuthorization(actionContext);
        }
        protected override bool IsAuthorized(HttpActionContext actionContext)
        {
            var test1 = System.Threading.Thread.CurrentPrincipal;
            var test2 = System.Security.Principal.WindowsIdentity.GetCurrent();
            ////string userName = actionContext.RequestContext.Principal.Name;/*  Web API v2  */
            string dingDingDingUserName = string.Empty;
            ApiController castController = actionContext.ControllerContext.Controller as ApiController;
            if (null != castController)
            {
                dingDingDingUserName = castController.User.Identity.Name;
            }
        string status = string.Empty;
        if (string.IsNullOrEmpty(dingDingDingUserName))
        {
            status = "Not Good.  No dingDingDingUserName";
        }
        else
        {
            status = "Finally!";
        }
            return true;
        }
    }
}

用这个属性装饰webapimethod。

    [IdentityWhiteListAuthorization]
    public IEnumerable<Product> GetAllProducts()
    {
        return products;
    }

(您必须解析名称空间)。

此时,您应该能够编译....和运行。

但是dingDingDingUserName将是string.Empty。(这篇文章的原始问题)

好吧. .

点击(左键点击一次)ProductsApp。

查看属性选项卡。(这不是"右键单击/属性:::这是显示的属性(默认会在VS的右下角),当你简单地左键单击ProductsApp.csproj.

您将看到几个设置,但有两个感兴趣:

Anonymous Authentication | Enabled
Windows Authentication | Enabled

(注意,以上是这些设置在VS GUI中显示的方式。它们在.csproj文件中显示如下)

           <IISExpressAnonymousAuthentication>enabled</IISExpressAnonymousAuthentication>
<IISExpressWindowsAuthentication>enabled</IISExpressWindowsAuthentication>

如果你设置

Anonymous Authentication | Disabled

(在.csproj中显示如下:

)
<IISExpressAnonymousAuthentication>disabled</IISExpressAnonymousAuthentication>
<IISExpressWindowsAuthentication>enabled</IISExpressWindowsAuthentication>

)

瞧!应该显示"dingDingDingName"的值。

我上面的链接…指向启用匿名身份验证的问题。

但是这里有一个很长的例子来展示直接影响…关于HttpClient.

这是我学到的另一个警告。

如果你不能改变

Anonymous Authentication Enabled/Disabled
Windows Authentication Enabled/Disabled

设置,然后你需要调整"主设置"。

在IIS Express中,它将位于如下文件中:

C: IISExpress config 文件上的用户名都用户 dangillmor applicationhost.config

"主设置"需要允许覆盖本地设置。

<sectionGroup name="security">
    <section name="anonymousAuthentication" overrideModeDefault="Allow" />
    <!-- Other Stuff -->
    <section name="windowsAuthentication" overrideModeDefault="Allow" />
    </sectionGroup>

身份验证本身需要在主级别上打开。

<security>
    <authentication>
    <anonymousAuthentication enabled="true" userName="" />
    <windowsAuthentication enabled="true">
                    <providers>
                                    <add value="Negotiate" />
                                    <add value="NTLM" />
                    </providers>
    </windowsAuthentication>
    </authentication>

(完整的IIS将在

中有类似的设置)

C: Windows System32系统 inetsrv config applicationHost.config

)

底线:

HttpClient可以发送运行HttpClient代码....的进程的WindowsIdentity使用HttpClientHandler 如果WebApiTier设置为WindowsAuthentication 匿名认证关闭。

Ok。我希望这对将来的人有所帮助。

最新更新