我有一个可以调用我的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;
}
答案 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,它不需要重新加载这些信息。
我做了很多搜索而没有找到任何东西,所以我的计划是这样做:
通过打开API连接验证自定义UserNamePasswordValidator中的登录详细信息。
在用户名下以连接池存储已打开的连接。
当调用我的自定义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中设置的自定义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;
}
然后我应该在需要时使用System.Threading.Thread.CurrentPrincipal或CurrentIdentity访问我的自定义标识和主体。
希望这在某种程度上有所帮助;我不确定这是最好的方法,但它是迄今为止我提出的最好的......
史蒂夫