WCF自定义验证器:如何从自定义验证器初始化“用户”对象

时间:2010-02-19 18:22:13

标签: c# wcf wcf-security

我有一个可以调用我的Oracle DB的自定义UserNamePasswordValidator。

此类派生自System.IdentityModel.Selectors.UserNamePasswordValidator,Validate()方法返回void。

我从数据库加载我的User对象,一旦验证了密码,我想隐藏我的“User”对象,以便服务可以在开展业务时访问它。在ASP.NET / Java领域,我会把它存入一个会话,或者我的整个Controller类。如何从WCF中的Validator执行此操作?

或者,换句话说,WCF的最佳做法是为服务设置自定义用户域对象。

更新:这就是我解决这个问题的方法。我在验证器期间缓存User对象,然后在AuthorizatinPolicy步骤中访问它。

  // this gets called after the custom authentication step where we loaded the User
  public bool Evaluate(EvaluationContext evaluationContext, ref object state)
  {
     // get the authenticated client identity
     IIdentity client = GetClientIdentity(evaluationContext);

     User user;
     OraclePasswordValidator.users.TryGetValue(client.Name, out user);
     if(user != null) {
        // set the custom principal
        evaluationContext.Properties["Principal"] = user;
        return true;
     }

     return false;
  }

2 个答案:

答案 0 :(得分:5)

我不是WCF专家,但从我到目前为止所阅读和实施的内容来看,执行此操作的“正确”方法是使用Validator 进行身份验证用户,然后实施IAuthorizationPolicy来执行实际的授权。因此,在授权策略中,您将在当前线程上设置自定义主体。

为了能够转发用户名/密码验证中的信息,您可以实现从UserNameSecurityTokenAuthenticator继承的安全令牌验证器。 SecurityTokenAuthenticator将首先调用验证器,如果验证成功,它可以添加自定义授权策略并通过构造函数将userinfo发送到策略。有点长的话:

public class CustomUsernameSecurityTokenAuthenticator : UserNameSecurityTokenAuthenticator
{
    protected override bool CanValidateTokenCore(System.IdentityModel.Tokens.SecurityToken token)
    {
        return (token is UserNameSecurityToken);
    }

    protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateTokenCore(SecurityToken token)
    {
        var authorizationPolicies = new List<IAuthorizationPolicy>();

        try
        {
            var userNameToken = token as UserNameSecurityToken;
            new CustomUserNameValidator().Validate(userNameToken.UserName, userNameToken.Password);

            var claims = new DefaultClaimSet(ClaimSet.System, new Claim(ClaimTypes.Name, userNameToken.UserName, Rights.PossessProperty));

            authorizationPolicies.Add(new CustomAuthorizationPolicy(claims));
        }
        catch (Exception)
        {
            authorizationPolicies.Add(new InvalidAuthorizationPolicy());
            throw;
        }
        return authorizationPolicies.AsReadOnly();
    }
}

这里有一篇文章描述了涉及的类的更多内容; http://blogs.msdn.com/card/archive/2007/10/04/how-identity-providers-can-show-custom-error-messages-in-cardspace.aspx

答案 1 :(得分:1)

我有完全相同的问题。

我使用API​​连接到我的底层Oracle数据库,并通过打开连接“验证”登录详细信息。

然后我想在某个地方存储这个连接(很简单,我将为所有不同的用户创建一个连接池),还要创建一个代表该用户的自定义Identity和Principal,这样一旦它到达我的自定义IAuthorizationPolicy,它不需要重新加载这些信息。

我做了很多搜索而没有找到任何东西,所以我的计划是这样做:

  1. 通过打开API连接验证自定义UserNamePasswordValidator中的登录详细信息。

  2. 在用户名下以连接池存储已打开的连接。

  3. 当调用我的自定义IAuthorizationPolicy.Evaluate()时,我将查看提供的通用标识:

    IIdentity GetClientIdentity(EvaluationContext evaluationContext)
    {
        object obj;
        if (!evaluationContext.Properties.TryGetValue("Identities", out obj))
            throw new Exception("No Identity found");
    
           IList<IIdentity> identities = obj as IList<IIdentity>;
           if (identities == null || identities.Count <= 0)
              throw new Exception("No Identity found");
    
           return identities[0];
       }
    
  4. (抱歉,我无法摆脱这种糟糕的HTML转义)

    1. 然后我根据IIdentity.Name从池中获取连接,使用此连接从数据库加载特定于用户的数据并将其存储在我在EvaluationContext中设置的自定义Identity和Principal中:

      public bool Evaluate(EvaluationContext evaluationContext, ref object state)
      {
          IIdentity identity = GetClientIdentity(evaluationContext);
          if (identity == null)
              throw new Exception();
      
              // These are my custom Identity and Principal classes
              Identity customIdentity = new Identity();
              Principal customPrincipal = new Principal(customIdentity);
              // populate identity and principal as required
              evaluationContext.Properties["Principal"] = customPrincipal;
              return true;
          }
      
    2. 然后我应该在需要时使用System.Threading.Thread.CurrentPrincipal或CurrentIdentity访问我的自定义标识和主体。

      希望这在某种程度上有所帮助;我不确定这是最好的方法,但它是迄今为止我提出的最好的......

      史蒂夫