WCF 中的自定义授权






4.86/5 (24投票s)
本文将展示如何实现自定义身份验证
引言
Windows Communication Foundation (WCF) 提供了在服务中实现授权的强大功能。
本文将向您展示如何实现自定义身份验证。 只有具有 ADMIN 角色的用户才能调用 Login()
方法。
注意:本文仅侧重于检查用户的角色。 您可以通过从数据库读取来修改此代码,以检查用户的用户名、密码和角色。
服务
创建自定义 Principal
它是为了向 WCF 提供您自定义的 IPrincipal
实现。 这使您有机会在每个请求的身份验证阶段之后隐式运行代码。 为此,您必须创建自己的自定义 principal 并将其返回给 WCF 管道。 然后,自定义 principal 将可从 Thread.CurrentPrincipal
访问到服务代码。 自定义 principal 允许完全自定义基于角色的安全,并为服务开发人员使用公开专门的安全逻辑。
编写自定义 principal 非常简单。 只需实现 IPrincipal
接口并添加您自己的自定义功能即可。 要将 principal 与 WCF 集成,您必须将 PrincipalPermissionMode
属性在 ServiceAuthorization
元素中设置为“custom
”,并提供一个负责创建 principal 并将其返回给 WCF 的授权策略。
一个 IPrincipal
派生类必须实现一个名为 IsInRole
的方法,您可以在其中传入一个角色名称并获得一个布尔响应。 此外,principal 还引用了它所包装的 identity 类。
class CustomPrincipal : IPrincipal
{
public bool IsInRole(string role)
{
EnsureRoles();
return _roles.Contains(role);
}
// read Role of user from database
protected virtual void EnsureRoles()
{
if (_identity.Name == "AnhDV")
_roles = new string[1] { "ADMIN" };
else
_roles = new string[1] { "USER" };
}
}
创建自定义授权策略
授权策略只是一个实现 System.IdentityModel.Policy.IAuthorizationPolicy
接口的类。 此接口的实现必须使用一个名为 Evaluate
的方法,该方法在每个请求上被调用。 在这里,您可以进入 WCF 服务安全上下文并设置自定义 principal。 下面的代码显示了一个自定义 principal 以及授权策略及其相应的配置条目。
AuthorizationPolicy:IAuthorizationPolicy
的重要部分是 Evaluate()
方法,该方法使用声明评估的上下文,尝试获取 PrimaryIdentity
。 此属性表示 WCF 在用户凭据验证期间发现的 identity。 然后,该方法将其转换为 CustomPrincipal
并将其附加到当前上下文中,从而使 principal 在当前运行的线程上可用。
class AuthorizationPolicy : IAuthorizationPolicy
{
Guid _id = Guid.NewGuid();
// this method gets called after the authentication stage
public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
// get the authenticated client identity
IIdentity client = GetClientIdentity(evaluationContext);
// set the custom principal
evaluationContext.Properties["Principal"] = new CustomPrincipal(client);
return true;
}
private 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];
}
}
<serviceAuthorization principalPermissionMode="Custom">
<authorizationPolicies>
<add policyType="WikiService.AuthorizationPolicy, App_Code/WikiSecurity" />
</authorizationPolicies>
</serviceAuthorization>
创建自定义验证器
如果服务本身,它现在将尝试针对 Windows 用户帐户验证提供的凭据,并且由于我们正在使用自己的自定义身份验证,这将失败!
密码内容位于以下重写中,我调用我的安全模块来检查用户和密码与数据库的对比
public class CustomValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
// validate arguments
if (string.IsNullOrEmpty(userName))
throw new ArgumentNullException("userName");
if (string.IsNullOrEmpty(password))
throw new ArgumentNullException("password");
// check the user credentials from database
//int userid = 0;
//CheckUserNameAndPassword(userName, password, out userid);
//if (0 == userid)
//throw new SecurityTokenException("Unknown username or password");
}
}
我们向服务表明我们想要自己的类 'CustomValidator
',该类位于程序集 'WikiSecurity
' 中,使用此配置来处理密码相关内容:
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="CustomValidator, App_Code/WikiSecurity" />
创建证书
如果您使用以下配置,则能够通过网络发送您的用户名和密码。
注意:您需要在服务器端创建并安装合适的证书以支持此功能,这是另一个故事...
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
您必须打开 \Certificate Setup Application\Certificate Setup\Source Code\Certificate Setup.sln 并运行它以创建您的临时证书示例: g2-anhdv-xp.fsoft.fpt.vn
并修改您的配置:
<serviceCertificate findValue="g2-anhdv-xp.fsoft.fpt.vn"
x509FindType="FindBySubjectName" />
您的服务代码:示例 WikiSecurity 类
只有具有 ADMIN 角色的用户才能调用 WikiSecurity 服务的 Login()
方法:
[PrincipalPermission(SecurityAction.Demand, Role = "ADMIN")]
public void Login()
{
}
客户端
然后,您可以在您的客户端代理上设置用户名和密码,如下所示
var factory = new ChannelFactory<IWikiSecurity>("*");
factory.Credentials.UserName.UserName = "AnhDV";//Do Viet Anh
factory.Credentials.UserName.Password = "anhdv";
var wikiProxy = factory.CreateChannel();
wikiProxy.Login();//-->Successful
var factory = new ChannelFactory<IWikiSecurity>("*");
factory.Credentials.UserName.UserName = "AnhDV1";//Do Viet Anh 1
factory.Credentials.UserName.Password = "anhdv";
var wikiProxy = factory.CreateChannel();
wikiProxy.Login();//Access Denied
希望这有所帮助。
享受并玩得开心。
历史
- 2009 年 3 月 4 日:初始发布