本文介绍了自定义的WebAPI System.Web.Http.AuthorizeAttribute不承认定制ClaimsPrincipal的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是到源$ C ​​$ C一个链接,我下面会解释。

我在哪里,我想设置一个(自定义)ClaimsPrincipal的问题。然后可以访问它在自定义System.Web.Mvc.AuthorizeAttribute(MVC),并自定义System.Web.Http.AuthorizeAttribute(的WebAPI)。

东西做工精细,并预期在定制System.Web.Mvc.AuthorizeAttribute。然而,在自定义System.Web.Http.AuthorizeAttribute(的WebAPI),当我搜索一个cookie我先前设置,自定义ClaimsPrincipal变成的GenericPrincipal。 (或者自定义ClaimsPrincipal永远不会被套)。

下面是触发code。

 保护无效Application_PostAuthenticateRequest(对象发件人,EventArgs的发送)
{
    / *交替为invokeFormsAuthenticationTicket code以下值..真或假,看问题* /
    布尔invokeFormsAuthenticationTicket code = TRUE;
          / *当真,则My​​CustomWebApiAuthorizeAttribute包含的GenericPrincipal的IPrincipal,而不是一个MyCustomClaimsPrincipal * /    如果(invokeFormsAuthenticationTicket code)
    {        的HttpCookie authCookie = Request.Cookies时[FormsAuthentication.FormsCookieName]        如果(authCookie!= NULL)
        {
            / *我们发现的cookie中的HomeController指数(它模拟一个用户成功登录设置* /
            的FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);            员工empFromCookie = JsonConvert.DeserializeObject<员工>(authTicket.UserData);
            如果(空== empFromCookie)
            {
                抛出新的ArgumentNullException(员工并未从Cookie的序列正确。);
            }            ////////的IIdentity IID =新的GenericIdentity(I_Started_In_Application_PostAuthenticateRequest.GenericIdentity);            / *所以我们知道用​​户登录的模拟通过,因为该cookie exists..lets设置MyCustomClaimsPrincipal * /
            MyCustomClaimsIdentity IID =新MyCustomClaimsIdentity(新GenericIdentity(\"I_Started_In_Application_PostAuthenticateRequest.MyCustomClaimsIdentity.invokeFormsAuthenticationTicket$c$c =真));
            MyCustomClaimsPrincipal PRINC =新MyCustomClaimsPrincipal(IID);            = Thread.CurrentPrincipal中PRINC;
            HttpContext.Current.User = PRINC;        }
        / *让我意识到,这里...在第一次通过这个code的....的Thread.CurrentPrincipal中和HttpContext.Current.User没有被设置* /
    }
    其他
    {
        MyCustomClaimsIdentity IID =新MyCustomClaimsIdentity(新GenericIdentity(\"I_Started_In_Application_PostAuthenticateRequest.MyCustomClaimsIdentity.invokeFormsAuthenticationTicket$c$c =假));
        MyCustomClaimsPrincipal PRINC =新MyCustomClaimsPrincipal(IID);        = Thread.CurrentPrincipal中PRINC;
        HttpContext.Current.User = PRINC;
    }
}

下面是自定义的mvc AuthorizeAttribute

 公共类MyCustomMvcAuthorizeAttribute:AuthorizeAttribute
    {
        公共MyCustomMvcAuthorizeAttribute()
        {}        公共覆盖无效OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
        {
            IPrincipal的X = HttpContext.Current.User;
            IPrincipal的Z = ClaimsPrincipal.Current;
            如果(x.GetType()!= typeof运算(MyCustomClaimsPrincipal))
            {
                抛出新ArgumentOutOfRangeException(IPrincipal的不是类型MyCustomClaimsPrincipal的预期);
                / *是否不'invokeFormsAuthenticationTicket code = true或= FALSE',这始终是一个MyCustomClaimsPrincipal * /
            }
        }        保护覆盖布尔AuthorizeCore(System.Web.HttpContextBase的HttpContext)
        {
            IPrincipal的X = HttpContext.Current.User;
            IPrincipal的Z = ClaimsPrincipal.Current;
            如果(x.GetType()!= typeof运算(MyCustomClaimsPrincipal))
            {
                抛出新ArgumentOutOfRangeException(IPrincipal的不是类型MyCustomClaimsPrincipal的预期);
                / *是否不'invokeFormsAuthenticationTicket code = true或= FALSE',这始终是一个MyCustomClaimsPrincipal * /
            }
            返回true;
        }
    }

下面是自定义的WebAPI定制AuthorizeAttribute

 公共类MyCustomWebApiAuthorizeAttribute:AuthorizeAttribute
    {
        公共MyCustomWebApiAuthorizeAttribute()
        {}        公共覆盖无效OnAuthorization(HttpActionContext ActionContext中)
        {
            布尔issueWasHit = FALSE;
            IPrincipal的X = HttpContext.Current.User;
            IPrincipal的Z = ClaimsPrincipal.Current;
            如果(x.GetType()!= typeof运算(MyCustomClaimsPrincipal))
            {
                //抛出新ArgumentOutOfRangeException(IPrincipal的不是类型MyCustomClaimsPrincipal的预期);
                字符串thisIsTheIssue =这是问题,当'invokeFormsAuthenticationTicket code =真正的',这是一个的GenericPrincipal,而不是一个MyCustomClaimsPrincipal。
                issueWasHit =真;
            }            base.OnAuthorization(ActionContext中);        }        保护覆盖布尔IsAuthorized(HttpActionContext ActionContext中)
        {
            布尔issueWasHit = FALSE;            IPrincipal的X = HttpContext.Current.User;
            IPrincipal的Z = ClaimsPrincipal.Current;            如果(x.GetType()!= typeof运算(MyCustomClaimsPrincipal))
            {
                //抛出新ArgumentOutOfRangeException(IPrincipal的不是类型MyCustomClaimsPrincipal的预期);
                字符串thisIsTheIssue =这是问题,当'invokeFormsAuthenticationTicket code =真正的',这是一个的GenericPrincipal,而不是一个MyCustomClaimsPrincipal。
                issueWasHit =真;
            }            返回true;
        }
    }

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

 公共类MyCustomClaimsIdentity:ClaimsIdentity
{
    公共MyCustomClaimsIdentity(的IIdentity身份)
        :基地(身份)
    {}
}公共类MyCustomClaimsPrincipal:ClaimsPrincipal
{
    公共MyCustomClaimsPrincipal(IID的IIdentity)
        :基座(IID)
    {}
}

于是我开始一个新的项目MVC4。它给你的HomeController(MVC)和Values​​Controller(的WebAPI apicontroller)。

下面是改变code对于那些开箱类。
HomeController.MyHomeControllerAlternateActionResult获取自定义属性。和索引通过设置一个cookie的global.asxa.cs(Application_PostAuthenticateRequest法)将拿起后模拟用户登录。

 公共类HomeController的:控制器
    {        公众的ActionResult指数()
        {            / *下面是模拟用户登录...主的是,它是在设置FormsAuthentication.FormsCookieName曲奇* /            员工E =新员工();
            e.SSN =999-99-9999;            字符串serializedUser = JsonConvert.SerializeObject(E,
                Formatting.None,
                新JsonSerializerSettings()
                {
                    ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
                });            / *所以这里的要点是随便扔东西,在FormsAuthentication.FormsCookieName曲奇* /
            的FormsAuthenticationTicket authTicket =新的FormsAuthenticationTicket(
                     1,
                     e.SSN,
                     DateTime.Now,
                     DateTime.Now.AddMinutes(15),
                     假,
                     serializedUser);            字符串encTicket = FormsAuthentication.Encrypt(authTicket);
            的HttpCookie faCookie =新的HttpCookie(FormsAuthentication.FormsCookieName,encTicket);
            Response.Cookies.Add(faCookie);            / * Cookie是到位,现在重定向* /            返回RedirectToAction(MyHomeControllerAlternateActionResult);
        }        [MyCustomMvcAuthorizeAttribute]
        公众的ActionResult MyHomeControllerAlternateActionResult()
        {
            IEnumerable的<串GT; modelViaWay1 = NULL;
            ////////的IEnumerable<串GT; modelViaWay2 = NULL;
            ////////的IEnumerable<串GT; modelViaWay3 = NULL;
            JSON字符串=的String.Empty;            使用(VAR的客户=新的HttpClient())
            {
                字符串values​​ControllerUrl = Url.RouteUrl(
                    DefaultApi
                    新{httproute =,控制器=值} / * * Values​​Controller /
                    Request.Url.Scheme
                );                modelViaWay1 =客户端
                            .GetAsync(values​​ControllerUrl)
                            。结果
                            .Content.ReadAsAsync<&IEnumerable的LT;串GT;>()结果。                ////////的Htt presponseMessage响应= client.GetAsync(values​​ControllerUrl)。结果;
                //////// JSON = response.Content.ReadAsStringAsync()结果。                //////// modelViaWay2 = JsonConvert.DeserializeObject<&IEnumerable的LT;串GT;>(JSON);
                //////// modelViaWay3 = response.Content.ReadAsAsync<&IEnumerable的LT;串GT;>()结果。            }            返回查看(); / *这将抛出一个视图MyHomeControllerAlternateActionResult或它的主人没有被发现或没有视图引擎支持搜索位置的错误,但是这不是本演示的地步。 * /
        }
    }

和值控制器,它只是增加了(的WebAPI)定制authorizeattribute

  [MyCustomWebApiAuthorizeAttribute]
    公共类Values​​Controller:ApiController
    {
        //获取API /值
        公共IEnumerable的<串GT;得到()
        {            VAR X = HttpContext.Current.User;
            变种Z = ClaimsPrincipal.Current;            返回新的字符串[] {值1,值2};
        }        //获取API /价值/ 5
        公共字符串GET(INT ID)
        {
            回到价值;
        }        // POST API /值
        公共无效后([FromBody]字符串值)
        {
        }        // PUT API /价值/ 5
        公共无效认沽(INT ID,[FromBody]字符串值)
        {
        }        //删除API /价值/ 5
        公共无效删除(INT ID)
        {
        }
    }

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

 公共类Employee
    {
        公众的System.Guid EmployeeUUID {搞定;组; }
        公共字符串SSN {搞定;组; }
        公共字符串名字{获得;组; }
        公共字符串名字{获得;组; }
        公共System.DateTime的? CREATEDATE {搞定;组; }
        公共System.DateTime的雇佣日期{搞定;组; }
    }

因此​​,这些都是主件(或下载code中的链接就什么都有了)。

我不明白,为什么我在的WebAPI定制authorizeattribute得到的GenericPrincipal,但是MVC定制authorizeattribute正常工作。

所以,如果你让我的样品中下载或工作时,触发机制是布尔invokeFormsAuthenticationTicket code =真;真假之间交替。

提示:在线路设置断点issueWasHit = TRUE;......这是哪里的问题时(杜)

下面是在任何版本的问题,我的情况下,参考/包的NuGet。该MVC4应用程序是对DOTNET的FW 4.5.2创建

 <参考包括=Microsoft.CSharp/>
<参考包括=系统/>
<参考包括=System.Data这/>
<参考包括=System.Data.DataSetExtensions/>
<参考包括=System.Data.Entity的/>
<参考包括=System.Drawing中/>
<参考包括=System.Web.DynamicData/>
<参考包括=System.Web.Entity/>
<参考包括=System.Web.ApplicationServices/>
<参考包括=System.ComponentModel.DataAnnotations/>
<参考包括=System.Web.Extensions程序/>
<参考包括=System.Web程序/>
<参考包括=System.Web.Abstractions/>
<参考包括=System.Web.Routing/>
<参考包括=的System.Xml/>
<参考包括=System.Configuration/>
<参考包括=的EntityFramework>
  < HintPath> .. \\包\\ EntityFramework.5.0.0 \\ lib目录\\ net45 \\ EntityFramework.dll< / HintPath>
< /参考和GT;
<参考包括=Microsoft.Web.Infrastructure,版本= 1.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL>
  <私人和GT;真< /私人和GT;
  <HintPath>..\\packages\\Microsoft.Web.Infrastructure.1.0.0.0\\lib\
et40\\Microsoft.Web.Infrastructure.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=Microsoft.Web.Mvc.FixedDisplayModes,版本= 1.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  <HintPath>..\\packages\\Microsoft.AspNet.Mvc.FixedDisplayModes.1.0.0\\lib\
et40\\Microsoft.Web.Mvc.FixedDisplayModes.dll</HintPath>
&LT; /参考和GT;
&lt;参考包含=Newtonsoft.Json&GT;
  &LT; HintPath&GT; .. \\包\\ Newtonsoft.Json.4.5.11 \\ lib目录\\ net40 \\ Newtonsoft.Json.dll&LT; / HintPath&GT;
&LT; /参考和GT;
&lt;参考包含=System.Net.Http&GT;
&LT; /参考和GT;
&lt;参考包括=System.Net.Http.Formatting,版本= 4.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  <HintPath>..\\packages\\Microsoft.AspNet.WebApi.Client.4.0.20710.0\\lib\
et40\\System.Net.Http.Formatting.dll</HintPath>
&LT; /参考和GT;
&lt;参考包含=System.Net.Http.WebRequest&GT;
&LT; /参考和GT;
&lt;参考包括=System.Web.Helpers,版本= 2.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  <HintPath>..\\packages\\Microsoft.AspNet.WebPages.2.0.20710.0\\lib\
et40\\System.Web.Helpers.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=System.Web.Http,版本= 4.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  <HintPath>..\\packages\\Microsoft.AspNet.WebApi.Core.4.0.20710.0\\lib\
et40\\System.Web.Http.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=System.Web.Http.WebHost,版本= 4.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  <HintPath>..\\packages\\Microsoft.AspNet.WebApi.WebHost.4.0.20710.0\\lib\
et40\\System.Web.Http.WebHost.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=System.Web.Mvc,版本= 4.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  <HintPath>..\\packages\\Microsoft.AspNet.Mvc.4.0.20710.0\\lib\
et40\\System.Web.Mvc.dll</HintPath>
&LT; /参考和GT;
&lt;参考包含=System.Web.Optimization&GT;
  <HintPath>..\\packages\\Microsoft.AspNet.Web.Optimization.1.0.0\\lib\
et40\\System.Web.Optimization.dll</HintPath>
&LT; /参考和GT;
&lt;参考包含=System.Web.Providers&GT;
  <HintPath>..\\packages\\Microsoft.AspNet.Providers.Core.1.2\\lib\
et40\\System.Web.Providers.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=System.Web.Razor,版本= 2.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  <HintPath>..\\packages\\Microsoft.AspNet.Razor.2.0.20715.0\\lib\
et40\\System.Web.Razor.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=System.Web.WebPages,版本= 2.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  <HintPath>..\\packages\\Microsoft.AspNet.WebPages.2.0.20710.0\\lib\
et40\\System.Web.WebPages.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=System.Web.WebPages.Deployment,版本= 2.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  <HintPath>..\\packages\\Microsoft.AspNet.WebPages.2.0.20710.0\\lib\
et40\\System.Web.WebPages.Deployment.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=System.Web.WebPages.Razor,版本= 2.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  <HintPath>..\\packages\\Microsoft.AspNet.WebPages.2.0.20710.0\\lib\
et40\\System.Web.WebPages.Razor.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=System.Web.Http.Tracing,版本= 4.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  <HintPath>..\\packages\\Microsoft.AspNet.WebApi.Tracing.4.0.0\\lib\
et40\\System.Web.Http.Tracing.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=System.Web.Http.OData,版本= 4.0.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  <HintPath>..\\packages\\Microsoft.AspNet.WebApi.OData.4.0.0\\lib\
et40\\System.Web.Http.OData.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=Microsoft.Data.Edm,版本= 5.2.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  <HintPath>..\\packages\\Microsoft.Data.Edm.5.2.0\\lib\
et40\\Microsoft.Data.Edm.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=Microsoft.Data.OData,版本= 5.2.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  <HintPath>..\\packages\\Microsoft.Data.OData.5.2.0\\lib\
et40\\Microsoft.Data.OData.dll</HintPath>
&LT; /参考和GT;
&lt;参考包括=System.Spatial,版本= 5.2.0.0,文化=中性公钥= 31bf3856ad364e35,的ProcessorArchitecture = MSIL&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  &LT; HintPath&GT; .. \\包\\ System.Spatial.5.2.0 \\ lib目录\\ net40 \\ System.Spatial.dll&LT; / HintPath&GT;
&LT; /参考和GT;
&lt;参考包括=System.Xml.Linq的/&GT;
&lt;参考包含=WebGrease&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  &LT; HintPath&GT; .. \\包\\ WebGrease.1.3.0 \\ lib目录\\ WebGrease.dll&LT; / HintPath&GT;
&LT; /参考和GT;
&lt;参考包含=Antlr3.Runtime&GT;
  &LT;私人和GT;真&LT; /私人和GT;
  &LT; HintPath&GT; .. \\包\\ WebGrease.1.3.0 \\ lib目录\\ Antlr3.Runtime.dll&LT; / HintPath&GT;
&LT; /参考和GT;


解决方案

因此​​,

 保护无效Application_PostAuthenticateRequest

方法运行两次。一旦MVC的,曾经为的WebAPI。饼干是空当Application_PostAuthenticateRequest方法的运行的WebAPI

此修复程序是饼干(或我的具体的cookie)传递到的WebAPI调用。
(注意/额外的code左右的CookieContainer旋转)

下面是MVC通过(具体)的cookie到的WebAPI

 公共类HomeController的:控制器
    {        公众的ActionResult指数()
        {            / *下面是模拟用户登录...主的是,它是在设置FormsAuthentication.FormsCookieName曲奇* /            员工E =新员工();
            e.SSN =999-99-9999;            字符串serializedUser = JsonConvert.SerializeObject(E,
                Formatting.None,
                新JsonSerializerSettings()
                {
                    ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
                });            / *所以这里的要点是随便扔东西,在FormsAuthentication.FormsCookieName曲奇* /
            的FormsAuthenticationTicket authTicket =新的FormsAuthenticationTicket(
                     1,
                     e.SSN,
                     DateTime.Now,
                     DateTime.Now.AddMinutes(15),
                     假,
                     serializedUser);            字符串encTicket = FormsAuthentication.Encrypt(authTicket);
            的HttpCookie faCookie =新的HttpCookie(FormsAuthentication.FormsCookieName,encTicket);
            Response.Cookies.Add(faCookie);            / * Cookie是到位,现在重定向* /            返回RedirectToAction(MyHomeControllerAlternateActionResult);
        }        [MyCustomMvcAuthorizeAttribute]
        公众的ActionResult MyHomeControllerAlternateActionResult()
        {
            IEnumerable的&LT;串GT; modelViaWay1 = NULL;
            ////////的IEnumerable&LT;串GT; modelViaWay2 = NULL;
            ////////的IEnumerable&LT;串GT; modelViaWay3 = NULL;
            JSON字符串=的String.Empty;            的CookieContainer的CookieContainer =新的CookieContainer();
            HttpClientHandler处理程序=新HttpClientHandler
            {
                UseCookies = TRUE,
                UseDefaultCredentials = TRUE,
                的CookieContainer =的CookieContainer
            };
            的foreach(在Request.Cookies时字符串cookiename)
            {
                如果(cookiename.Equals(FormsAuthentication.FormsCookieName,StringComparison.OrdinalIgnoreCase))
                {
                    VAR饼干= Request.Cookies时[cookiename]
                    cookieContainer.Add(新的Cookie(cookie.Name,cookie.Value,cookie.Path,本地主机));
                }
            }            使用(VAR的客户=新的HttpClient(处理))
            {
                字符串values​​ControllerUrl = Url.RouteUrl(
                    DefaultApi
                    新{httproute =,控制器=值} / * * Values​​Controller /
                    Request.Url.Scheme
                );                modelViaWay1 =客户端
                            .GetAsync(values​​ControllerUrl)
                            。结果
                            .Content.ReadAsAsync&LT;&IEnumerable的LT;串GT;&GT;()结果。                ////////的Htt presponseMessage响应= client.GetAsync(values​​ControllerUrl)。结果;
                //////// JSON = response.Content.ReadAsStringAsync()结果。                //////// modelViaWay2 = JsonConvert.DeserializeObject&LT;&IEnumerable的LT;串GT;&GT;(JSON);
                //////// modelViaWay3 = response.Content.ReadAsAsync&LT;&IEnumerable的LT;串GT;&GT;()结果。            }            返回查看(); / *这将抛出一个视图MyHomeControllerAlternateActionResult或它的主人没有被发现或没有视图引擎支持搜索位置的错误,但是这不是本演示的地步。 * /
        }
    }

现在我Application_PostAuthenticateRequest看起来恢复正常。

 保护无效Application_PostAuthenticateRequest(对象发件人,EventArgs的发送)
    {
            的HttpCookie authCookie = Request.Cookies时[FormsAuthentication.FormsCookieName]            如果(authCookie!= NULL)
            {
                / *我们发现的cookie中的HomeController指数(它模拟一个用户成功登录设置* /
                的FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);                员工empFromCookie = JsonConvert.DeserializeObject&LT;员工&GT;(authTicket.UserData);
                如果(空== empFromCookie)
                {
                    抛出新的ArgumentNullException(员工并未从Cookie的序列正确。);
                }
                / *所以我们知道用​​户登录的模拟通过,因为该cookie exists..lets设置MyCustomClaimsPrincipal * /
                MyCustomClaimsIdentity IID =新MyCustomClaimsIdentity(新GenericIdentity(\"I_Started_In_Application_PostAuthenticateRequest.MyCustomClaimsIdentity.invokeFormsAuthenticationTicket$c$c =真));
                MyCustomClaimsPrincipal PRINC =新MyCustomClaimsPrincipal(IID);                = Thread.CurrentPrincipal中PRINC;
                HttpContext.Current.User = PRINC;            }    }

底线。

当一个MVC和的WebAPI托管(联合举办)在同一层.... cookie不会自动发送到的WebAPI。这是我的问题。

Here is a link to the source code that I will explain below.

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

I have an issue where I am trying to set a (custom) ClaimsPrincipal. And then have access to it in a custom System.Web.Mvc.AuthorizeAttribute (mvc) and also a custom System.Web.Http.AuthorizeAttribute (webapi).

Things work fine and as expected in the custom System.Web.Mvc.AuthorizeAttribute. However, in the custom System.Web.Http.AuthorizeAttribute (webapi), when I search for a cookie I set earlier, the custom ClaimsPrincipal turns into a GenericPrincipal. (Or perhaps the custom ClaimsPrincipal never gets "set").

Here is the "trigger" code.

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;
    }
}

Here is the custom 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;
        }
    }

Here is the custom webApi custom 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;
        }
    }

Here are my custom IIdentity (ClaimsIdentity) and IPrincipal (ClaimsPrincipal)

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

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

So I started with a new MVC4 project. Which gives you a HomeController (mvc) and a ValuesController (webapi apicontroller).

Here is the altered code for those out of the box classes.HomeController.MyHomeControllerAlternateActionResult gets the custom attribute. and the "Index" simulates a user-login by setting a cookie that the global.asxa.cs (Application_PostAuthenticateRequest method) will pick up on later.

   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. */
        }
    }

And the values controller, which simply adds the (webapi) custom 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)
        {
        }
    }

Oh yeah, I added a model....

 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; }
    }

So those are the main pieces (or download the code in the link to have it all).

I cannot figure out why I get a GenericPrincipal in the webapi custom authorizeattribute, but the mvc custom authorizeattribute works fine.

So if you get the download or work in my samples, the triggering mechanism is "bool invokeFormsAuthenticationTicketCode = true;" and alternating between true and false.

Hints: set breakpoints on the lines "issueWasHit = true;"....that is where the issue occurs (duh).

Here are my references/nuget packages in case of any version questions. The MVC4 app was created against 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>
解决方案

So the

    protected void Application_PostAuthenticateRequest

method runs twice. Once for the MVC, once for the WebApi. The cookies are empty when the Application_PostAuthenticateRequest method runs for the WebApi.

The fix was to pass the cookies (or my specific cookie) to the WebApi call.(Note the/extra code revolving around CookieContainer )

Here is the Mvc passing (the specific) cookie to the 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. */
        }
    }

And now my Application_PostAuthenticateRequest looks normal again.

    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;

            }

    }

Bottom Line.

When an MVC and WebApi are hosted (cohosted) in the same layer....cookies are not automatically sent to the WebApi. That was my issue.

这篇关于自定义的WebAPI System.Web.Http.AuthorizeAttribute不承认定制ClaimsPrincipal的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-27 06:12