WebApi Custom System.Web.Http.AuthorizeAttribute无法识别自定义ClaimsPrincipal

时间:2015-09-18 00:46:37

标签: c# asp.net asp.net-mvc asp.net-mvc-4 asp.net-web-api

以下是我将在下面解释的源代码链接。

http://www.filehosting.org/file/details/510152/CustomPrincipalSimpleSOFExample.zip

我有一个问题,我正在尝试设置(自定义)ClaimsPrincipal。然后可以在自定义的System.Web.Mvc.AuthorizeAttribute(mvc)以及自定义的System.Web.Http.AuthorizeAttribute(webapi)中访问它。

在自定义System.Web.Mvc.AuthorizeAttribute中,事情正常并且正如预期的那样。但是,在自定义System.Web.Http.AuthorizeAttribute(webapi)中,当我搜索之前设置的cookie时,自定义ClaimsPrincipal变为GenericPrincipal。 (或者也许自定义的ClaimsPrincipal永远不会得到"设置")。

这是"触发器"代码。

protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
    /* alternate the below value for invokeFormsAuthenticationTicketCode..  either true or false to see the issue */
    bool invokeFormsAuthenticationTicketCode = true;
          /* when "true", the MyCustomWebApiAuthorizeAttribute contains a GenericPrincipal IPrincipal, not a  MyCustomClaimsPrincipal*/



    if (invokeFormsAuthenticationTicketCode)
    {

        HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];

        if (authCookie != null)
        {
            /* we found the cookie set in the HomeController-Index (which simulates a user-successful login */
            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);

            Employee empFromCookie = JsonConvert.DeserializeObject<Employee>(authTicket.UserData);
            if (null == empFromCookie)
            {
                throw new ArgumentNullException("Employee did not serialize from Cookie correctly.");
            }

            ////////IIdentity iid = new GenericIdentity("I_Started_In_Application_PostAuthenticateRequest.GenericIdentity");

            /* so we know the simulation of the user-login "passed" because the cookie exists..lets set the MyCustomClaimsPrincipal */
            MyCustomClaimsIdentity iid = new MyCustomClaimsIdentity(new GenericIdentity("I_Started_In_Application_PostAuthenticateRequest.MyCustomClaimsIdentity.invokeFormsAuthenticationTicketCode = true"));
            MyCustomClaimsPrincipal princ = new MyCustomClaimsPrincipal(iid);

            Thread.CurrentPrincipal = princ;
            HttpContext.Current.User = princ;

        }
        /* so I realize that here...on the first pass through of this code.... the "Thread.CurrentPrincipal" and "HttpContext.Current.User" are not being set */
    }
    else
    {
        MyCustomClaimsIdentity iid = new MyCustomClaimsIdentity(new GenericIdentity("I_Started_In_Application_PostAuthenticateRequest.MyCustomClaimsIdentity.invokeFormsAuthenticationTicketCode = false"));
        MyCustomClaimsPrincipal princ = new MyCustomClaimsPrincipal(iid);

        Thread.CurrentPrincipal = princ;
        HttpContext.Current.User = princ;
    }
}

这是自定义Mvc AuthorizeAttribute

   public class MyCustomMvcAuthorizeAttribute : AuthorizeAttribute
    {
        public MyCustomMvcAuthorizeAttribute()
        { }

        public override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
        {
            IPrincipal x = HttpContext.Current.User;
            IPrincipal z = ClaimsPrincipal.Current;
            if (x.GetType() != typeof(MyCustomClaimsPrincipal))
            {
                throw new ArgumentOutOfRangeException("IPrincipal was not of type MyCustomClaimsPrincipal as expected");
                /* whether or not 'invokeFormsAuthenticationTicketCode = true or = false', this is always a MyCustomClaimsPrincipal */
            }
        }

        protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
        {
            IPrincipal x = HttpContext.Current.User;
            IPrincipal z = ClaimsPrincipal.Current;
            if (x.GetType() != typeof(MyCustomClaimsPrincipal))
            {
                throw new ArgumentOutOfRangeException("IPrincipal was not of type MyCustomClaimsPrincipal as expected");
                /* whether or not 'invokeFormsAuthenticationTicketCode = true or = false', this is always a MyCustomClaimsPrincipal */
            }
            return true;
        }
    }

这是自定义webApi自定义AuthorizeAttribute

 public class MyCustomWebApiAuthorizeAttribute : AuthorizeAttribute
    {
        public MyCustomWebApiAuthorizeAttribute()
        { }

        public override void OnAuthorization(HttpActionContext actionContext)
        {
            bool issueWasHit = false;
            IPrincipal x = HttpContext.Current.User;
            IPrincipal z = ClaimsPrincipal.Current;
            if(x.GetType() != typeof(MyCustomClaimsPrincipal))
            {
                //throw new ArgumentOutOfRangeException("IPrincipal was not of type MyCustomClaimsPrincipal as expected");
                string thisIsTheIssue = "This is the issue.  When 'invokeFormsAuthenticationTicketCode = true', this is a GenericPrincipal, not a MyCustomClaimsPrincipal";
                issueWasHit = true;
            }

            base.OnAuthorization(actionContext);

        }

        protected override bool IsAuthorized(HttpActionContext actionContext)
        {
            bool issueWasHit = false;

            IPrincipal x = HttpContext.Current.User;
            IPrincipal z = ClaimsPrincipal.Current;

            if (x.GetType() != typeof(MyCustomClaimsPrincipal))
            {
                //throw new ArgumentOutOfRangeException("IPrincipal was not of type MyCustomClaimsPrincipal as expected");
                string thisIsTheIssue = "This is the issue.  When 'invokeFormsAuthenticationTicketCode = true', this is a GenericPrincipal, not a MyCustomClaimsPrincipal";
                issueWasHit = true;
            }

            return true;
        }
    }

以下是我的自定义IIdentity(ClaimsIdentity)和IPrincipal(ClaimsPrincipal)

public class MyCustomClaimsIdentity : ClaimsIdentity
{
    public MyCustomClaimsIdentity(IIdentity identity)
        : base(identity)
    { }
}

public class MyCustomClaimsPrincipal : ClaimsPrincipal
{
    public MyCustomClaimsPrincipal(IIdentity iid)
        : base(iid)
    { }
}

所以我开始使用一个新的MVC4项目。这给你一个HomeController(mvc)和一个ValuesController(webapi apicontroller)。

这是开箱即用类的改变代码。 HomeController.MyHomeControllerAlternateActionResult获取自定义属性。和&#34;索引&#34;通过设置global.asxa.cs(Application_PostAuthenticateRequest方法)稍后将提取的cookie来模拟用户登录。

   public class HomeController : Controller
    {

        public ActionResult Index()
        {

            /* the below is simulating a user-login... the main-thing is that it is setting a "FormsAuthentication.FormsCookieName" cookie */

            Employee e = new Employee();
            e.SSN = "999-99-9999";

            string serializedUser = JsonConvert.SerializeObject(e,
                Formatting.None,
                new JsonSerializerSettings()
                {
                    ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
                });

            /* so the point here is to just throw something in the "FormsAuthentication.FormsCookieName" cookie */
            FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                     1,
                     e.SSN,
                     DateTime.Now,
                     DateTime.Now.AddMinutes(15),
                     false,
                     serializedUser);

            string encTicket = FormsAuthentication.Encrypt(authTicket);
            HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
            Response.Cookies.Add(faCookie);

            /* cookie is in place, now redirect */

            return RedirectToAction("MyHomeControllerAlternateActionResult");
        }

        [MyCustomMvcAuthorizeAttribute]
        public ActionResult MyHomeControllerAlternateActionResult()
        {
            IEnumerable<string> modelViaWay1 = null;
            ////////IEnumerable<string> modelViaWay2 = null;
            ////////IEnumerable<string> modelViaWay3 = null;
            string json = string.Empty;

            using (var client = new HttpClient())
            {
                string valuesControllerUrl = Url.RouteUrl(
                    "DefaultApi",
                    new { httproute = "", controller = "Values" }, /* ValuesController */
                    Request.Url.Scheme
                );

                modelViaWay1 = client
                            .GetAsync(valuesControllerUrl)
                            .Result
                            .Content.ReadAsAsync<IEnumerable<string>>().Result;

                ////////HttpResponseMessage response = client.GetAsync(valuesControllerUrl).Result;
                ////////json = response.Content.ReadAsStringAsync().Result;

                ////////modelViaWay2 = JsonConvert.DeserializeObject<IEnumerable<string>>(json);
                ////////modelViaWay3 = response.Content.ReadAsAsync<IEnumerable<string>>().Result;

            }

            return View(); /* this will throw a "The view 'MyHomeControllerAlternateActionResult' or its master was not found or no view engine supports the searched locations" error, but that's not the point of this demo. */
        }
    }

值控制器,它只是添加(webapi)自定义authorizeattribute

 [MyCustomWebApiAuthorizeAttribute]
    public class ValuesController : ApiController
    {
        // GET api/values
        public IEnumerable<string> Get()
        {

            var x = HttpContext.Current.User;
            var z = ClaimsPrincipal.Current;

            return new string[] { "value1", "value2" };
        }

        // GET api/values/5
        public string Get(int id)
        {
            return "value";
        }

        // POST api/values
        public void Post([FromBody]string value)
        {
        }

        // PUT api/values/5
        public void Put(int id, [FromBody]string value)
        {
        }

        // DELETE api/values/5
        public void Delete(int id)
        {
        }
    }

哦是的,我添加了一个模型......

 public class Employee
    {
        public System.Guid EmployeeUUID { get; set; }
        public string SSN { get; set; }
        public string LastName { get; set; }
        public string FirstName { get; set; }
        public System.DateTime? CreateDate { get; set; }
        public System.DateTime HireDate { get; set; }
    }

所以这些是主要部分(或者下载链接中的代码以完成所有操作)。

我无法弄清楚为什么我在webapi自定义authorizeattribute中获得GenericPrincipal,但mvc自定义authorizeattribute工作正常。

因此,如果您在我的示例中获得下载或工作,则触发机制为&#34; bool invokeFormsAuthenticationTicketCode = true;&#34;并且在真与假之间交替。

提示:在行上设置断点&#34; issueWasHit = true;&#34; ....即发生问题的地方(duh)。

以下是我的参考/ nuget包,以防任何版本问题。 MVC4应用程序是针对DotNet FW 4.5.2

创建的
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data.Entity" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Abstractions" />
<Reference Include="System.Web.Routing" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="EntityFramework">
  <HintPath>..\packages\EntityFramework.5.0.0\lib\net45\EntityFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.Mvc.FixedDisplayModes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\Microsoft.AspNet.Mvc.FixedDisplayModes.1.0.0\lib\net40\Microsoft.Web.Mvc.FixedDisplayModes.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
  <HintPath>..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http">
</Reference>
<Reference Include="System.Net.Http.Formatting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <HintPath>..\packages\Microsoft.AspNet.WebApi.Client.4.0.20710.0\lib\net40\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.WebRequest">
</Reference>
<Reference Include="System.Web.Helpers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <HintPath>..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http.WebHost, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <HintPath>..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.20710.0\lib\net40\System.Web.Http.WebHost.dll</HintPath>
</Reference>
<Reference Include="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll</HintPath>
</Reference>
<Reference Include="System.Web.Optimization">
  <HintPath>..\packages\Microsoft.AspNet.Web.Optimization.1.0.0\lib\net40\System.Web.Optimization.dll</HintPath>
</Reference>
<Reference Include="System.Web.Providers">
  <HintPath>..\packages\Microsoft.AspNet.Providers.Core.1.2\lib\net40\System.Web.Providers.dll</HintPath>
</Reference>
<Reference Include="System.Web.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\Microsoft.AspNet.Razor.2.0.20715.0\lib\net40\System.Web.Razor.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Deployment, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http.Tracing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\Microsoft.AspNet.WebApi.Tracing.4.0.0\lib\net40\System.Web.Http.Tracing.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http.OData, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\Microsoft.AspNet.WebApi.OData.4.0.0\lib\net40\System.Web.Http.OData.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Data.Edm, Version=5.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\Microsoft.Data.Edm.5.2.0\lib\net40\Microsoft.Data.Edm.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Data.OData, Version=5.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\Microsoft.Data.OData.5.2.0\lib\net40\Microsoft.Data.OData.dll</HintPath>
</Reference>
<Reference Include="System.Spatial, Version=5.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
  <Private>True</Private>
  <HintPath>..\packages\System.Spatial.5.2.0\lib\net40\System.Spatial.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="WebGrease">
  <Private>True</Private>
  <HintPath>..\packages\WebGrease.1.3.0\lib\WebGrease.dll</HintPath>
</Reference>
<Reference Include="Antlr3.Runtime">
  <Private>True</Private>
  <HintPath>..\packages\WebGrease.1.3.0\lib\Antlr3.Runtime.dll</HintPath>
</Reference>

1 个答案:

答案 0 :(得分:0)

所以

    protected void Application_PostAuthenticateRequest

方法运行两次。一次为MVC,一次为WebApi。当Application_PostAuthenticateRequest方法为WebApi运行时,cookie为空。

修复是将cookie(或我的特定cookie)传递给WebApi调用。 (注意围绕CookieContainer的/额外代码)

这是Mvc传递(特定)cookie到WebApi

   public class HomeController : Controller
    {

        public ActionResult Index()
        {

            /* the below is simulating a user-login... the main-thing is that it is setting a "FormsAuthentication.FormsCookieName" cookie */

            Employee e = new Employee();
            e.SSN = "999-99-9999";

            string serializedUser = JsonConvert.SerializeObject(e,
                Formatting.None,
                new JsonSerializerSettings()
                {
                    ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
                });

            /* so the point here is to just throw something in the "FormsAuthentication.FormsCookieName" cookie */
            FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
                     1,
                     e.SSN,
                     DateTime.Now,
                     DateTime.Now.AddMinutes(15),
                     false,
                     serializedUser);

            string encTicket = FormsAuthentication.Encrypt(authTicket);
            HttpCookie faCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
            Response.Cookies.Add(faCookie);

            /* cookie is in place, now redirect */

            return RedirectToAction("MyHomeControllerAlternateActionResult");
        }

        [MyCustomMvcAuthorizeAttribute]
        public ActionResult MyHomeControllerAlternateActionResult()
        {
            IEnumerable<string> modelViaWay1 = null;
            ////////IEnumerable<string> modelViaWay2 = null;
            ////////IEnumerable<string> modelViaWay3 = null;
            string json = string.Empty;

            CookieContainer cookieContainer = new CookieContainer();
            HttpClientHandler handler = new HttpClientHandler
            {
                UseCookies = true,
                UseDefaultCredentials = true,
                CookieContainer = cookieContainer
            };
            foreach (string cookiename in Request.Cookies)
            {
                if (cookiename.Equals(FormsAuthentication.FormsCookieName, StringComparison.OrdinalIgnoreCase))
                {
                    var cookie = Request.Cookies[cookiename];
                    cookieContainer.Add(new Cookie(cookie.Name, cookie.Value, cookie.Path, "localhost"));
                }
            }

            using (var client = new HttpClient(handler))
            {
                string valuesControllerUrl = Url.RouteUrl(
                    "DefaultApi",
                    new { httproute = "", controller = "Values" }, /* ValuesController */
                    Request.Url.Scheme
                );

                modelViaWay1 = client
                            .GetAsync(valuesControllerUrl)
                            .Result
                            .Content.ReadAsAsync<IEnumerable<string>>().Result;

                ////////HttpResponseMessage response = client.GetAsync(valuesControllerUrl).Result;
                ////////json = response.Content.ReadAsStringAsync().Result;

                ////////modelViaWay2 = JsonConvert.DeserializeObject<IEnumerable<string>>(json);
                ////////modelViaWay3 = response.Content.ReadAsAsync<IEnumerable<string>>().Result;

            }

            return View(); /* this will throw a "The view 'MyHomeControllerAlternateActionResult' or its master was not found or no view engine supports the searched locations" error, but that's not the point of this demo. */
        }
    }

现在我的Application_PostAuthenticateRequest再次显示正常。

    protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
    {
            HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];

            if (authCookie != null)
            {
                /* we found the cookie set in the HomeController-Index (which simulates a user-successful login */
                FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);

                Employee empFromCookie = JsonConvert.DeserializeObject<Employee>(authTicket.UserData);
                if (null == empFromCookie)
                {
                    throw new ArgumentNullException("Employee did not serialize from Cookie correctly.");
                }


                /* so we know the simulation of the user-login "passed" because the cookie exists..lets set the MyCustomClaimsPrincipal */
                MyCustomClaimsIdentity iid = new MyCustomClaimsIdentity(new GenericIdentity("I_Started_In_Application_PostAuthenticateRequest.MyCustomClaimsIdentity.invokeFormsAuthenticationTicketCode = true"));
                MyCustomClaimsPrincipal princ = new MyCustomClaimsPrincipal(iid);

                Thread.CurrentPrincipal = princ;
                HttpContext.Current.User = princ;

            }

    }

底线。

当MVC和WebApi在同一层中托管(共同)时...... Cookie不会自动发送到WebApi。这是我的问题。