在WCF中填充PrimaryIdentity

时间:2010-05-13 21:03:01

标签: .net wcf security

我正在使用简单的HTTP标头将令牌传递给WCF服务进行身份验证(WCF服务需要使用basicHTTPBinding,因此我很遗憾无法使用canned ws-security实现)。我想填充PrimaryIdentity对象,以便WCF服务可以检查它以确定经过身份验证的用户。

问题是{I}属性在我尝试填充时是只读的。我已经尝试使用SecurityTokenAuthenticators和IAuthorizationPolicy对象来设置身份信息,但该路由似乎需要使用消息级安全性(例如总是发送用户名和密码),这不是我想要的。

有人能说明如何设置PrimaryIdentity字段吗?

3 个答案:

答案 0 :(得分:12)

您可以创建一个展示IAuthorizationPolicy的类。 WCF解析所有标识令牌(X509,WS-Security用户名/传递等),并将它们放在Evaluate函数中的evaluationContext.Properties [“Identities”]中。这将返回一个List给你。如果将此列表替换为包含您自己的实现IIdentity的类的列表,则WCF会将其读入ServiceSecurityContext.Current.PrimaryIdentity。请确保该列表仅包含一个项目,否则您将混淆WCF,PrimaryIdentity.Name将是空字符串。

var myIdentity = new MyIdentity("MyId", otherstuff);
evaluationContext.Properties["Identities"] = new List<IIdentity> {myIdentity};

此外,您可能希望在替换它们之前处理/读取任何标记。

var identities = evaluationContext.Properties.ContainsKey("Identities")
                                 ? (List<IIdentity>) evaluationContext.Properties["Identities"]
                                 : new List<IIdentity>();

var genericIdentity = identities.Find(x => x.AuthenticationType == "MyUserNamePasswordValidator");

genericIdentity.Name - &gt;包含来自WSS用户名令牌的用户名。

如果您使用的是WS-Security Username令牌并且不希望任何默认的WCF验证,则需要UsernamePasswordValidator(http://msdn.microsoft.com/en-us/library/system.identitymodel.selectors.usernamepasswordvalidator.aspx)。因为,我们有一个DataPower设备在消息到达我们的服务之前验证令牌,我们不需要验证用户名和密码。在我们的例子中,它只返回true。

答案 1 :(得分:5)

PrimaryIdentity不打算由您填充 - 这是WCF运行时的工作,用于确定谁在调用,并相应地设置PrimaryIdentity

因此,如果您使用Windows凭据进行呼叫,那么您将在其中存储WindowsIdentity;如果您使用的是ASP.NET成员资格提供程序,则会将该调用程序存储到PrimaryIdentity

实际设置此方法的唯一方法是创建自己的自定义服务器端身份验证机制并将其插入WCF。

请参阅:

答案 2 :(得分:4)

这对我有用...首先设置主机的身份验证行为(这里通过代码显示,但也可以在配置中完成):

ServiceAuthorizationBehavior author = Description.Behaviors.Find<ServiceAuthorizationBehavior>();
author.ServiceAuthorizationManager = new FormCookieServiceAuthorizationManager();
author.PrincipalPermissionMode = PrincipalPermissionMode.Custom;
author.ExternalAuthorizationPolicies = new List<IAuthorizationPolicy> { new CustomAuthorizationPolicy() }.AsReadOnly();

然后是辅助类

  internal class FormCookieServiceAuthorizationManager : ServiceAuthorizationManager
  {
     public override bool CheckAccess(OperationContext operationContext)
     {
        ParseFormsCookie(operationContext.RequestContext.RequestMessage);
        return base.CheckAccess(operationContext);
     }
     private static void ParseFormsCookie(Message message)
     {
        HttpRequestMessageProperty httpRequest = message.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
        if (httpRequest == null) return;

        string cookie = httpRequest.Headers[HttpRequestHeader.Cookie];
        if (string.IsNullOrEmpty(cookie)) return;

        string regexp = Regex.Escape(FormsAuthentication.FormsCookieName) + "=(?<val>[^;]+)";
        var myMatch = Regex.Match(cookie, regexp);
        if (!myMatch.Success) return;

        string cookieVal = myMatch.Groups["val"].ToString();
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(cookieVal);
        Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(authTicket.Name), new string[0]);
     }
  }
  internal class CustomAuthorizationPolicy : IAuthorizationPolicy
  {
     static readonly string _id = Guid.NewGuid().ToString();
     public string Id
     {
        get { return _id; }
     }

     public bool Evaluate(EvaluationContext evaluationContext, ref object state)
     {
        evaluationContext.Properties["Principal"] = Thread.CurrentPrincipal;
        evaluationContext.Properties["Identities"] = new List<IIdentity> { Thread.CurrentPrincipal.Identity };
        return true;
     }

     public ClaimSet Issuer
     {
        get { return ClaimSet.System; }
     }
  }

对于设置AspNetCompatibility时,FormCookieServiceAuthorizationManager稍微简单一些:

 internal class FormCookieServiceAuthorizationManager : ServiceAuthorizationManager
 {
    public override bool CheckAccess(OperationContext operationContext)
    {
       Thread.CurrentPrincipal = HttpContext.Current.User;
       return base.CheckAccess(operationContext);
    }
 }