对于基本身份验证,我根据Darin Dimitrov的回答中所示的示例实现了一个自定义HttpMessageHandler:https://stackoverflow.com/a/11536349/270591

代码使用用户名和角色创建GenericPrincipal类型的实例principal,然后将此主体设置为线程的当前主体:

Thread.CurrentPrincipal = principal;

稍后在ApiController方法中,可以通过访问控制器User属性来读取主体:

public class ValuesController : ApiController
{
    public void Post(TestModel model)
    {
        var user = User; // this should be the principal set in the handler
        //...
    }
}

这似乎运行得很好,直到我最近添加了一个使用Task库的自定义MediaTypeFormatter,如下所示:

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

(我用这种方法从一些示例代码中以ReadFromStreamAsync中的Task.Factory.StartNew开始一项任务.这是错误的吗?可能是问题的唯一原因?)

现在,"有时"——对我来说这似乎是随机的——控制器方法中的User主体不再是我在MessageHandler中设置的主体,即用户名、Authenticated标志和角色都丢失了.原因似乎是自定义MediaTypeFormatter导致MessageHandler和controller方法之间的线程发生变化.我通过比较MessageHandler和controller方法中Thread.CurrentThread.ManagedThreadId的值来确认这一点."有时"他们是不同的,然后校长就"迷路了".

我现在寻找了一种替代设置Thread.CurrentPrincipal的方法,以某种方式将主体从自定义MessageHandler安全地转移到控制器方法,并在this blog post个请求属性中使用:

request.Properties.Add(HttpPropertyKeys.UserPrincipalKey,
    new GenericPrincipal(identity, new string[0]));

我想对此进行测试,但在最近的WebApi版本(候选版本和上周的最终版本)中,HttpPropertyKeys类(位于名称空间System.Web.Http.Hosting中)似乎不再具有UserPrincipalKey属性.

我的问题是:如何更改上面的最后一个代码片段,使其与当前的WebAPI版本兼容?或者一般情况下:如何在自定义MessageHandler中设置用户主体,并在控制器方法中可靠地访问它?

Edit

提到here,"HttpPropertyKeys.UserPrincipalKey……解析成“MS_UserPrincipal”",所以我试着用:

request.Properties.Add("MS_UserPrincipal",
    new GenericPrincipal(identity, new string[0]));

但它并没有像我预期的那样工作:ApiController.User属性不包含添加到上面Properties集合中的主体.

推荐答案

这里提到了在新线程上丢失本金的问题:

http://leastprivilege.com/2012/06/25/important-setting-the-client-principal-in-asp-net-web-api/

Important: Setting the Client Principal in ASP.NET Web API

由于深埋在ASP.NET中的一些不幸机制,设置 Web API Web托管中的Thread.CurrentRule是不够的.

在ASP中托管时.网,线.CurrentPrincipal可能会被覆盖

这里:http://aspnetwebstack.codeplex.com/workitem/264美元

现在,您需要为用户主体设置以下两项内容 如果使用自定义消息处理程序在 Web托管方案.

IPrincipal principal = new GenericPrincipal(
    new GenericIdentity("myuser"), new string[] { "myrole" });
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;

我已经将最后一行HttpContext.Current.User = principal(需要using System.Web;)添加到消息处理程序中,ApiController中的User属性现在始终具有正确的主体,即使线程由于MediaTypeFormatter中的任务而更改.

Edit

只是强调一下:只有当WebApi托管在ASP中时,才有必要设置当前用户的原则HttpContext.NET/IIS.对于自托管,这是不必要的(而且不可能,因为HttpContext是ASP.NET构造,自托管时不存在).

Asp.net相关问答推荐

如何使用 Get-Process powershell 命令通过端口号获取进程的 ID

InvalidOperationException:在程序集上找不到UserSecretsIdAttribute

ASP.NET Web 应用程序 (.NET Framework) 与 ASP.NET Core Web 应用程序 (.NET Framework) 之间的差异

用户NT AUTHORITY\NETWORK SERVICE登录失败

如何使用三个按钮是否和取消显示确认alert ,如 MS Word 中所示

在 cookie 中存储多个值

如何在txt文件中保存异常?

UseSqlServer 方法缺少 MVC 6

来自 IP 地址的经度和纬度值

ASP.NET MVC4 异步控制器 - 为什么要使用?

带有文件名的 ASP.net MVC4 WebApi 路由

用于验证的数据注释,至少一个必填字段?

单独装配和路由中的控制器

如何在 ASP.NET MVC 中设置时区?

.NET AJAX 调用 ASMX 或 ASPX 或 ASHX?

Web API 必填参数

会话存储在客户端还是服务器端

ASP.NET 应用程序状态与静态对象

Docker 中的 ASPNETCORE_ENVIRONMENT

解析器错误:此处不允许使用_Default,因为它没有扩展类System.Web.UI.Page和 MasterType 声明