异步设置Thread.CurrentPrincipal?

10
使用ASP.NET WebAPI,在身份验证期间设置了Thread.CurrentPrincipal,以便控制器稍后可以使用ApiController.User属性。
如果该身份验证步骤变成异步的(以便查询另一个系统),则会丢失对CurrentPrincipal的任何更改(当调用者的await恢复同步上下文时)。
这是一个非常简化的示例(在实际代码中,身份验证发生在操作筛选器中):
using System.Diagnostics;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;

public class ExampleAsyncController : System.Web.Http.ApiController
{
    public async Task GetAsync()
    {
        await AuthenticateAsync();

        // The await above saved/restored the current synchronization
        // context, thus undoing the assignment in AuthenticateAsync(). 
        Debug.Assert(User is GenericPrincipal);
    }

    private static async Task AuthenticateAsync()
    {
        // Save the current HttpContext because it's null after await.
        var currentHttpContext = System.Web.HttpContext.Current;

        // Asynchronously determine identity.
        await Task.Delay(1000);
        var identity = new GenericIdentity("<name>");

        var roles = new string[] { };
        Thread.CurrentPrincipal = new GenericPrincipal(identity, roles);
        currentHttpContext.User = Thread.CurrentPrincipal;
    }
}

如何在异步函数中设置Thread.CurrentPrincipal,使得调用者的await在恢复同步上下文时不会丢弃该变化?


1
我不确定您的问题,但最好在调用控制器之前对其进行身份验证。在我的WebAPI项目中,我喜欢设置一个DelegatingHandler,它可以在到达控制器之前进行身份验证。 - Matthew
@Matthew,我同意,这就是真正的代码工作方式,动作过滤器在控制器方法被调用之前处理身份验证。我排除了动作过滤器以简化我的示例,但无论哪种情况,问题都是相同的。 - Jon-Eric
根据这个stackoverflow的答案,如果你设置了CurrentPrincipal,它将会持续到调用线程:https://dev59.com/PGcs5IYBdhLWcg3w84dp#12460170,否则我不确定如何解决你的问题。 - Matthew
1个回答

11

您还需要将HttpContext.Current.User设置好。请参见这个答案这篇博客文章获取更多信息。

更新:还要确保您在.NET 4.5上运行,并将 UserTaskFriendlySynchronizationContext 设置为 true


感谢你的帮助Stephen。我添加了设置 HttpContext.Current.User(并更新了我问题中的代码),但没有起作用。问题没有改变。 - Jon-Eric
对我来说它绝对有效。我在ValuesController中使用上述代码创建了一个新的WebAPI项目,它完美地运行着。后续问题:您是否在.NET 4.5上运行,并且是否将“UseTaskFriendlySynchronizationContext”设置为true? - Stephen Cleary
将“UseTaskFriendlySynchronizationContext”设置为true解决了问题!感谢您抽出时间进行调查! - Jon-Eric
不用谢!我编辑了答案,让它更完整。奇怪的是,在我的机器上,无论 UTFSC 设置为 true 还是 false 都能工作。我不确定在哪些确切的条件下 UTFSC 会有所不同,但我很高兴它能帮上忙! - Stephen Cleary
经过进一步调查,史蒂芬,我发现我的示例之所以有效,是因为存在 await Task.Delay(1000)。我特意提出了一个新问题,请参考 https://dev59.com/gmQn5IYBdhLWcg3wrouL。如果您有时间,我将非常感激您的任何建议。 - Jon-Eric

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接