如何在 ServiceAuthorizationManager 中设置身份/用户名?

Posted

技术标签:

【中文标题】如何在 ServiceAuthorizationManager 中设置身份/用户名?【英文标题】:How to set identity/username in 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 是只读的。我认为需要更复杂的代码。

【问题讨论】:

【参考方案1】:

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

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

    正如@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 解决方案中。经过几次简单的测试后,它似乎工作至今。

    如果身份不是真正需要的,而只是用户名,则将用户名写入新请求标头的简单“技巧”/“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"];
        // ...
    
    

    另一个更底层的选项是编写一个自定义的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。再次感谢您的帮助!

以上是关于如何在 ServiceAuthorizationManager 中设置身份/用户名?的主要内容,如果未能解决你的问题,请参考以下文章

如何在表单提交后保留文本(如何在提交后不删除自身?)

如何在异步任务中调用意图?或者如何在 onPostExecute 中开始新的活动?

在 Avkit 中如何使用这三行代码,以及如何将音乐静音”

如何在 JDBC 中启动事务?

如何在 Fragment 中调用 OnActivityResult 以及它是如何工作的?

如何使用 Firebase 在 Web 上托管 Flutter?它的效果如何?