Sla*_*uma 50 .net asp.net security asp.net-mvc asp.net-web-api
对于基本身份验证,我已经实现了一个HttpMessageHandler
基于Darin Dimitrov在这里回答的示例的自定义:https://stackoverflow.com/a/11536349/270591
代码创建具有用户名和角色principal
的类型实例,GenericPrincipal
然后将此主体设置为线程的当前主体:
Thread.CurrentPrincipal = principal;
Run Code Online (Sandbox Code Playgroud)
稍后在ApiController
方法中,可以通过访问controllers User
属性来读取主体:
public class ValuesController : ApiController
{
public void Post(TestModel model)
{
var user = User; // this should be the principal set in the handler
//...
}
}
Run Code Online (Sandbox Code Playgroud)
这似乎工作正常,直到我最近添加了一个MediaTypeFormatter
使用该Task
库的自定义,如下所示:
public override Task<object> ReadFromStreamAsync(Type type, Stream readStream,
HttpContent content, IFormatterLogger formatterLogger)
{
var task = Task.Factory.StartNew(() =>
{
// some formatting happens and finally a TestModel is returned,
// simulated here by just an empty model
return (object)new TestModel();
});
return task;
}
Run Code Online (Sandbox Code Playgroud)
(我有这种方法来启动一项任务Task.Factory.StartNew
的ReadFromStreamAsync
一些示例代码.它是错误的,也许对这个问题的唯一原因吗?)
现在,"有时" - 对我而言似乎是随机的 - User
控制器方法中的主体不再是我在MessageHandler中设置的主体,即用户名,Authenticated
标志和角色都丢失了.原因似乎是自定义MediaTypeFormatter导致MessageHandler和控制器方法之间的线程发生了变化.我已经通过比较Thread.CurrentThread.ManagedThreadId
MessageHandler和控制器方法中的值来证实了这一点."有时"他们是不同的,然后校长"迷失".
我现在看一下设置Thread.CurrentPrincipal
以某种方式将主体安全地从自定义MessageHandler转移到控制器方法的替代方法,并在此博客中使用请求属性:
request.Properties.Add(HttpPropertyKeys.UserPrincipalKey,
new GenericPrincipal(identity, new string[0]));
Run Code Online (Sandbox Code Playgroud)
我想测试一下,但似乎HttpPropertyKeys
该类(在命名空间中System.Web.Http.Hosting
)UserPrincipalKey
在最近的WebApi版本中不再具有属性(发布候选版本和上周的最终版本).
我的问题是:如何更改上面的最后一个代码段,以便与当前的WebAPI版本一起使用?或者通常:如何在自定义MessageHandler中设置用户主体并在控制器方法中可靠地访问它?
编辑
它提到在这里说," HttpPropertyKeys.UserPrincipalKey
......解析为“MS_UserPrincipal”
",所以我试图用:
request.Properties.Add("MS_UserPrincipal",
new GenericPrincipal(identity, new string[0]));
Run Code Online (Sandbox Code Playgroud)
但它不能像我预期的那样工作:该ApiController.User
属性不包含添加到Properties
上面集合中的主体.
Sla*_*uma 75
这里提到了丢失新线程上的主体的问题:
http://leastprivilege.com/2012/06/25/important-setting-the-client-principal-in-asp-net-web-api/
重要:在ASP.NET Web API中设置客户端主体
由于深入ASP.NET中的一些不幸机制,在Web API Web托管中设置Thread.CurrentPrincipal是不够的.
在ASP.NET中托管时,在创建新线程时,可能会使用HttpContext.Current.User覆盖Thread.CurrentPrincipal.这意味着您必须在线程和HTTP上下文中设置主体.
在这里:http://aspnetwebstack.codeplex.com/workitem/264
今天,如果使用自定义消息处理程序在Web托管方案中执行身份验证,则需要为用户主体设置以下两项.
Run Code Online (Sandbox Code Playgroud)IPrincipal principal = new GenericPrincipal( new GenericIdentity("myuser"), new string[] { "myrole" }); Thread.CurrentPrincipal = principal; HttpContext.Current.User = principal;
我已经将最后一行HttpContext.Current.User = principal
(需求using System.Web;
)添加到消息处理程序中,并且现在的User
属性ApiController
总是具有正确的主体,即使线程由于MediaTypeFormatter中的任务而发生了更改.
编辑
只是为了强调它:HttpContext
只有当WebApi托管在ASP.NET/IIS中时才需要设置当前用户的主体.对于自托管,它不是必需的(并且不可能,因为HttpContext
它是ASP.NET构造,并且在自托管时不存在).
要避免上下文切换,请尝试使用a TaskCompletionSource<object>
而不是在自定义中手动启动另一个任务MediaTypeFormatter
:
public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
{
var tcs = new TaskCompletionSource<object>();
// some formatting happens and finally a TestModel is returned,
// simulated here by just an empty model
var testModel = new TestModel();
tcs.SetResult(testModel);
return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)
使用自定义MessageHandler,您可以MS_UserPrincipal
通过调用以下HttpRequestMessageExtensionMethods.SetUserPrincipal
定义的扩展方法来添加属性System.ServiceModel.Channels
:
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var user = new GenericPrincipal(new GenericIdentity("UserID"), null);
request.SetUserPrincipal(user);
return base.SendAsync(request, cancellationToken);
}
Run Code Online (Sandbox Code Playgroud)
请注意,这只会将此属性添加到Request的Properties集合中,它不会更改附加到ApiController的User.