Пользовательский валидатор 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, он не ' Не нужно перезагружать эту информацию.
Я много искал и ничего не нашел, поэтому я планирую сделать это:
Проверьте данные для входа в пользовательском UserNamePasswordValidator, открыв соединение API.
Хранить открытое соединение в пуле соединений под именем пользователя.
Когда мой пользовательский 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)
Затем я получаю соединение из пула на основе 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.
Надеюсь, это поможет каким-то образом; Я не уверен, что это лучший способ сделать это, но это лучшее, что я придумал до сих пор...
Стив