WebApi Custom System.Web.Http.AuthorizeAttribute does not re
本文关键字:does not re AuthorizeAttribute Http Custom System Web WebApi | 更新日期: 2023-09-27 18:13:10
这是源代码的链接,我将在下面解释。
http://www.filehosting.org/file/details/510152/CustomPrincipalSimpleSOFExample.zip我有一个问题,我试图设置一个(自定义)claimprincipal。然后在自定义System.Web.Mvc.AuthorizeAttribute (mvc)和System.Web.Http.AuthorizeAttribute (webapi)中访问它。
在自定义System.Web.Mvc.AuthorizeAttribute中工作得很好,正如预期的那样。然而,在自定义System.Web.Http.AuthorizeAttribute (webapi)中,当我搜索我之前设置的cookie时,自定义claimprincipal变成了GenericPrincipal。(或者自定义claimprincipal永远不会被"设置")。
这是"触发器"代码。
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 (claimprincipal)
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获取自定义属性。"Index"通过设置一个cookie来模拟用户登录,稍后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. */
}
}
values控制器,它只是添加了(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; }
}
这些是主要的部分(或下载链接中的代码以获得全部)。
我不知道为什么我得到一个GenericPrincipal在webapi自定义authorizeattribute,但mvc自定义authorizeattribute工作良好。
因此,如果您下载或在我的示例中工作,触发机制是"bool invokeFormsAuthenticationTicketCode = true;"并在true和false之间交替。
提示:在"issueWasHit = true;"....上设置断点这就是问题发生的地方。
这里是我的参考/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>
所以
protected void Application_PostAuthenticateRequest
方法运行两次。一次用于MVC,一次用于WebApi。当WebApi的Application_PostAuthenticateRequest方法运行时,cookie是空的。
解决方法是将cookie(或我的特定cookie)传递给WebApi调用。(注意CookieContainer周围的/extra代码)
这是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。这是我的问题