【问题标题】:How to set identity/username in ServiceAuthorizationManager?如何在 ServiceAuthorizationManager 中设置身份/用户名?
【发布时间】:2011-11-10 03:00:10
【问题描述】:

我在服务器端有一个 WCF 4.0 REST 服务(托管在 IIS 中)和一个 Android 客户端。 Android 客户端在自定义 HTTP 标头中发送加密的安全令牌,以便对用户进行身份验证。我已经实现了一个自定义ServiceAuthorizationManager,它从标头中提取安全令牌。令牌包含我可以从令牌中读取的用户名:

public class MyAuthorizationManager : ServiceAuthorizationManager
{
    protected override bool CheckAccessCore(OperationContext operationContext)
    {
        var requestMessage = operationContext.RequestContext.RequestMessage;
        var requestProperty = (HttpRequestMessageProperty)requestMessage
            .Properties[HttpRequestMessageProperty.Name];
        var token = requestProperty.Headers["X-MyCustomHeader"];
        if (!string.IsNullOrEmpty(token))
        {
            var userName = GetUserNameFromToken(token);
            if (!string.IsNullOrEmpty(userName))
            {
                // How to save userName now so that I can 
                // retrieve it in the service operations?
                return true;
            }
        }
        return false;
    }
}

现在,我的问题是我在各种服务操作中还需要经过身份验证的用户的名称(主要是访问用户配置文件数据),我打算这样检索它:

public void MyServiceOperation()
{
    string userName = OperationContext.Current
        .ServiceSecurityContext.PrimaryIdentity.Name;

    // check profile store for that userName and do something depending on
    // profile settings
}

如何在CheckAccessCore 中设置此用户名?

像这样非常幼稚的试验......

operationContext.ServiceSecurityContext.PrimaryIdentity.Name = userName;

...不起作用,因为PrimaryIdentity.Name 是只读的。我认为需要更复杂的代码。

【问题讨论】:

    标签: c# .net wcf wcf-security


    【解决方案1】:

    经过一番研究,我没有找到在ServiceAuthorizationManager.CheckAccessCore 中设置身份的方法。当用户的身份已经设置(可能是“匿名”(IsAuthenticatedfalse))并且无法再更改时,此方法似乎在处理管道中调用得太晚了。 ServiceAuthorizationManager 用于授权,而不是身份验证,因此它是实施自定义身份验证的错误位置。

    我终于找到了解决我的问题的三种可能方法:

    1. 正如@TheCodeKing 的答案中链接的文章中所解释的,使用 WCF REST Starter Kit 提供了编写自定义 RequestInterceptor 的选项,该选项足够早地挂接到管道中,允许访问传入的请求并允许例如,根据自定义 HTTP 标头设置用户的身份。不幸的是,WCF REST Starter Kit 是旧的(基于 WCF 3.5),并且显然已经放弃了开发。它的一些功能已被合并到 WCF 4.0 中,但有些还没有,RequestInterceptor 就是其中之一。尽管如此,我现在已经使用了这个解决方案,并将 Starter Kit 中的 Microsoft.ServiceModel.Web 程序集混合到我的 WCF 4.0 解决方案中。经过几次简单的测试后,它似乎工作至今。

    2. 如果身份不是真正需要的,而只是用户名,则将用户名写入新请求标头的简单“技巧”/“hack”可以工作(也在CheckAccessCore中):

      // ...
      var userName = GetUserNameFromToken(token);
      if (!string.IsNullOrEmpty(userName))
      {
          requestProperty.Headers["X-UserName"] = userName;
          return true;
      }
      // ...
      

      然后在服务方法中:

      public void MyServiceOperation()
      {
          string userName = WebOperationContext.Current.IncomingRequest
              .Headers["X-UserName"];
          // ...
      }
      
    3. 另一个更底层的选项是编写一个自定义的HttpModule,它拦截传入的请求并设置身份。 Microsoft Pattern & Practices 团队的here 是一个示例(请参阅本文中间的“HTTP 模块代码”示例)。

    【讨论】:

    • 等等,所以不可能以任何方式检索带有operationContext.ServiceSecurityContext.PrimaryIdentity.Name 的用户名?
    【解决方案2】:

    看看this的文章。它有设置IAuthorizationPolicy 实例的示例。通过创建您自己的实现,您可以控制在上下文中传递的IPrincipalIIdentity 实例的创建。这一切都来自一个服务拦截器。

    internal class AuthorizationPolicyFactory
    {
       public virtual IAuthorizationPolicy Create(Credentials credentials)
       {
          var genericIdentity = new GenericIdentity(credentials.UserName);
          var genericPrincipal = new GenericPrincipal(genericIdentity,
                                                      new string[] { });
          return new PrincipalAuthorizationPolicy(genericPrincipal);
       }
    }
    

    【讨论】:

    • 我记得前几天我看过这篇文章,但没有详细了解,因为它需要 WCF REST starter Kit。但实际上套件中的RequestInterceptor 可能是解决我问题的关键。我没有找到另一种方法来在管道中及早拦截和评估消息(除非可能是自定义 HttpModule)安全上下文和身份已经创建之前。遗憾的是,这个有用的钩子没有内置到 WCF4 中。我将试验文章中的代码。感谢您的提示!
    • 是的,它有点隐蔽,不是吗:)
    • 我现在就这样走了。我不喜欢使用旧的 WCF REST Starter Kit,但其他选项(请参阅我自己的答案)甚至更少。 IMO,MS 仍有很多机会改进 WCF REST。再次感谢您的帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-02
    • 2019-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-14
    相关资源
    最近更新 更多