Пользовательский валидатор WCF: как инициализировать объект "Пользователь" из пользовательского валидатора

У меня есть рабочий пользовательский UserNamePasswordValidator, который вызывает мою базу данных Oracle.

Этот класс является производным от System.IdentityModel.Selectors.UserNamePasswordValidator, а метод Validate() возвращает void.

Я загружаю свой объект User из базы данных, и после того, как пароль подтвержден, я хочу спрятать свой объект "User", чтобы служба могла получить к нему доступ при работе. В ASP.NET / Java Land я бы спрятал его в сеанс или, возможно, мой общий класс Controller. Как мне сделать это из валидатора в WCF?

Или, другими словами, какова лучшая практика в земле 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 ответа

Решение

Я не эксперт WCF, но из того, что я прочитал и реализовал до сих пор, "правильный" способ сделать это - использовать Validator аутентифицировать пользователя, а затем реализовать IAuthorizationPolicy сделать фактическое разрешение. Таким образом, в политике авторизации вы будете устанавливать свой пользовательский принципал в текущем потоке.

Чтобы иметь возможность пересылать информацию из проверки имени пользователя / пароля, вы можете реализовать аутентификатор токена безопасности, который наследуется от UserNameSecurityTokenAuthenticator, SecurityTokenAuthenticator сначала вызывает валидатор и, если проверка прошла успешно, он может добавить вашу собственную политику авторизации и отправить информацию о пользователе в политику через конструктор. Что-то длинное в этом роде:

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

У меня точно такая же проблема.

Я использую API для соединения с базой данных Oracle, и я "проверяю" данные для входа, открывая соединение.

Затем я хочу сохранить это соединение где-нибудь (достаточно просто, я создам пул соединений для всех разных пользователей), но также создаю пользовательскую личность и принципала, представляющих этого пользователя, так что, как только он доберется до моей пользовательской IAuthorizationPolicy, он не ' Не нужно перезагружать эту информацию.

Я много искал и ничего не нашел, поэтому я планирую сделать это:

  1. Проверьте данные для входа в пользовательском UserNamePasswordValidator, открыв соединение API.

  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];
       }
    

(извините, я не могу избавиться от этого плохого выхода из HTML)

  1. Затем я получаю соединение из пула на основе IIdentity.Name, использую это соединение для загрузки пользовательских данных из базы данных и сохраняю их в пользовательских идентификаторах и принципалах, которые я установил в EvaluationContext:

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

Тогда я должен иметь доступ к своему пользовательскому идентификатору и принципалу всякий раз, когда мне это нужно, используя System.Threading.Thread.CurrentPrincipal или CurrentIdentity.

Надеюсь, это поможет каким-то образом; Я не уверен, что это лучший способ сделать это, но это лучшее, что я придумал до сих пор...

Стив

Другие вопросы по тегам