1

我有一个 Web API 2 项目,我们正在使用基本授权。客户端在授权标头中发送用户名:密码 - 我们将其提取并连接到 LDAP 服务器以

  1. 验证用户
  2. 验证他们的密码,然后
  3. 从他们的 LDAP 响应中获取一个属性值并将其传递给请求的 IPrincipal。

问题是,当我通过System.Web.Http.ApiController.User.Identity.Name访问控制器中的IPrincipal时,我看到传入的值并不总是正确的!

示例:在消息处理程序或过滤器中,我将 IPrincipal 设置为:

    public class BasicAuthenticationAttribute : Attribute, IAuthenticationFilter
    {

        ...other code...

        private IPrincipal ReturnPrincipal(string UserID, List<string> roles)
        {
            roles.Add("SomeRole");

            // UserID = 7144 or 8899 (load testing)
            var identity = new GenericIdentity(UserID, "Basic");
            var principal = new GenericPrincipal(identity, roles.ToArray());

            return principal;
        }
    }

编辑:这是我们在过滤器中设置主体的方式:

        var principal = await AuthenticateAsync(UserID, password, cancellationToken);

        if (principal == null)
        {
            // Authentication was attempted but failed. Set ErrorResult to indicate an error.
            context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
        }
        else
        {
            // Authentication was attempted and succeeded. Set Principal to the authenticated user.
            context.Principal = principal;
        }

在请求的生命周期后期,在控制器中,我尝试像这样访问这个值:

    protected bool IsAuthorized(string UserID)
    {
        return User.Identity.Name == UserID;
    }

所以这里发生的是我从ApiController.User.Identity.Name获取User.Identity.Name,在负载测试下,我同时发送 2 个线程并行请求 - 当我这样做时,请求对象显示正确URL 中的 ID,但是,过滤器中设置的 IPrincipal 不正确。这是一个具有预期结果的固定测试,所以这永远不会发生。如果它错开电话,问题就不会出现!如果同时发送呼叫,则问题的发生率为 100%。

这是我调用服务的方式:

            var task1 = Task.Run(() => CallServerInParallel(requests2, api));
            var task2 = Task.Run(() => CallServerInParallel2(requests2, api));

            Task.WaitAll(task1, task2);

...

    private static void CallServerInParallel2(List<int> requests2, string api)
    {
        Parallel.ForEach(requests2, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, async p =>
        {
            var client = new RemoteClient(api);
            client.AddAuthHeader("d2JsMTE4NTgzOUBbdGVhY2NlcHQuY29tOnRlc3QxMjM0");
            var response = await client.Get<dynamic>("CustomerSite/5537");
        });
    }

    private static void CallServerInParallel2(List<int> requests2, string api)
    {
        Parallel.ForEach(requests2, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, async p =>
        {
            var client = new RemoteClient(api);
            client.AddAuthHeader("d2JsMTE4NTgzOUBkdGVhY1NlcHQuY29tOnRlc3QxMjM0");
            var response = await client.Get<dynamic>("CustomerSite/5538");
        });
    }

如何访问请求的正确 IPrincipal.User.Identity.Name?

4

1 回答 1

0

如此处所引用:设置主体

我通过确保在设置上下文主体后在我的过滤器中还执行以下操作来解决此类问题

如果您的应用程序执行任何自定义身份验证逻辑,则必须在两个位置设置主体:

  • Thread.CurrentPrincipal。此属性是在 .NET 中设置线程主体的标准方法。
  • HttpContext.Current.User。此属性特定于 ASP.NET。

以下代码显示了如何设置主体:

private void SetPrincipal(IPrincipal principal)
{
    Thread.CurrentPrincipal = principal;
    if (HttpContext.Current != null)
    {
        HttpContext.Current.User = principal;
    }
}

对于虚拟主机,您必须在两个地方设置主体;否则安全上下文可能会变得不一致。但是,对于自托管,HttpContext.Current 为空。因此,为确保您的代码与主机无关,请在分配给 HttpContext.Current 之前检查 null,如图所示。

var principal = await AuthenticateAsync(UserID, password, cancellationToken);

if (principal == null) {
    // Authentication was attempted but failed. Set ErrorResult to indicate an error.
    context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
} else {
    // Authentication was attempted and succeeded. Set Principal to the authenticated user.
    context.Principal = principal;
    SetPrincipal(principal);
}
于 2016-02-03T18:01:40.327 回答