简体   繁体   English

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

[英]WCF Custom Validator: How to initialize a “User” object from custom validator

I have a working custom UserNamePasswordValidator that calls into my Oracle DB. 我有一个工作的自定义UserNamePasswordValidator,它可以调用我的Oracle DB。

This class derives from System.IdentityModel.Selectors.UserNamePasswordValidator and the Validate() method returns void. 此类从System.IdentityModel.Selectors.UserNamePasswordValidator派生,并且Validate()方法返回void。

I load my User object from the database, and once the password is validated, I want to stash my "User" object so the service can access it when going about its business. 我从数据库中加载了User对象,并且在验证密码之后,我想存放我的“ User”对象,以便服务在进行业务时可以访问它。 In ASP.NET / Java land I would stash it into a session, or perhaps my overall Controller class. 在ASP.NET/Java领域中,我会将其存储在会话中,或者存储在整个Controller类中。 How do I do this from the Validator in WCF? 如何从WCF中的验证器执行此操作?

Or, in other words, what is the best practice in WCF land to set a custom User domain object for the service. 换句话说,在WCF中最好的做法是为服务设置自定义用户域对象。

Update: This is how I've worked around it. 更新:这就是我的解决方法。 I cache the User object during the validator, then access it later in the AuthorizatinPolicy step. 我在验证器期间缓存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;
  }

I'm not a WCF expert, but from what I've read and implemented so far, the 'correct' way to do this would be to use the Validator to authenticate the user, and then implement an IAuthorizationPolicy to do the actual authorization . 我不是WCF专家,但是到目前为止,我已经阅读并实现了该方法,“正确”的方法是使用Validator对用户进行身份验证 ,然后实施IAuthorizationPolicy进行实际授权 So it would be in the authorization policy that you'll set your custom principal on the current thread. 因此,将在授权策略中在当前线程上设置自定义主体。

To be able to forward information from the username/password validation, you can implement a security token authenticator that inherits from UserNameSecurityTokenAuthenticator . 为了能够转发用户名/密码验证中的信息,您可以实现一个继承自UserNameSecurityTokenAuthenticator的安全令牌身份验证器。 The SecurityTokenAuthenticator will first call the validator and if validation succeeds, it can add your custom authorization policy and send userinfo to the policy through the constructor. SecurityTokenAuthenticator将首先调用验证器,如果验证成功,它将添加您的自定义授权策略,并通过构造函数将userinfo发送到该策略。 Something a long the lines of this: 很长的话:

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();
    }
}

There's an article here that describes a bit more around the involved classes; 这里有一篇文章介绍了有关所涉及的类的更多信息。 http://blogs.msdn.com/card/archive/2007/10/04/how-identity-providers-can-show-custom-error-messages-in-cardspace.aspx http://blogs.msdn.com/card/archive/2007/10/04/how-identity-providers-can-show-custom-error-messages-in-cardspace.aspx

I have exactly the same issue. 我有完全一样的问题。

I am using an API to connect to my underlying Oracle Database, and I "validate" logon details by opening a connection. 我使用API​​连接到底层Oracle数据库,并通过打开连接来“验证”登录详细信息。

I then want to store this connection somewhere (easy enough, I will create a connection pool for all the different users), but also create a custom Identity and Principal representing this user, so that once it gets to my custom IAuthorizationPolicy, it doesn't need to reload this information. 然后,我想将此连接存储在某个地方(很容易,我将为所有不同的用户创建一个连接池),而且还要创建一个代表该用户的自定义身份和委托人,以便一旦到达我的自定义IAuthorizationPolicy,它就不会无需重新加载此信息。

I have done a lot of searching and not found anything so my plan is to do this: 我已经做了很多搜索,但没有发现任何东西,所以我的计划是这样做:

  1. Validate login details in custom UserNamePasswordValidator by opening API connection. 通过打开API连接来验证自定义UserNamePasswordValidator中的登录详细信息。

  2. Store opened connection in connection pool under the user name. 将打开的连接存储在用户名下的连接池中。

  3. When my custom IAuthorizationPolicy.Evaluate() is called, I will look at the generic identity provided: 调用我的自定义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]; } 

(sorry I can't get rid of this poor HTML escaping) (对不起,我无法摆脱这种不良的HTML转义)

  1. I then grab a connection from the pool based on the IIdentity.Name, use this connection to load up user-specific data from the database and store this in a custom Identity and Principal which I set in the EvaluationContext: 然后,我基于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; } 

Then I should have access to my custom identity and principal whenever I need it by using System.Threading.Thread.CurrentPrincipal or CurrentIdentity. 然后,只要需要,我就可以使用System.Threading.Thread.CurrentPrincipal或CurrentIdentity访问我的自定义身份和主体。

Hope this helps in some way; 希望这会有所帮助; I'm not sure it's the best way to go about it, but it's the best I've come up with so far... 我不确定这是最好的解决方法,但是到目前为止,这是我提出的最好的方法...

Steve 史蒂夫

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM