WCF Security之MembershipProvider+RoleProvider方案
对于web应用(包括web站点及web服务)的安全,我们首先想到的和见到的是,让客户提供凭据(最常见的是用户名和密码),然后服务端对客户提供的凭据进行验证,验证通过后,在具体的方法调用或页面请求时,根据验证通过的客户身份进行授权检查,授权通过,则执行客户的请求;反之则拒绝客户的请求。这就是一般验证及授权的思路。
如果这样还不能安全要求,那只好再启用传输层加密,即SSL了。实际上在WCF中,验证方式也被分为两个层次:Transport和Message。在传输层加密数据,在消息层提供凭据验证及授权。本文就准备介绍客户端在消息层传入用户凭据,服务端通过MembershipProvider进行验证,通过RoleProvider进行授权,然后辅以传输层加密的方案。
这个方案针对于WCF服务,为了便于客户调用,而又加强传输层安全,所以选择了SSL,另外调用WCF服务又需要提供身份验证及授权,尤其针对凭据信息自定义的要求,客户端凭据选择UserName类型。
这个方案的好处是什么呢?TransportWithMessageCredential确保了传输上的安全,并提供了客户端凭据。而对客户端凭据的验证和授权支持自定义方式,所以不管用什么实现rbac,都可以使用这套方案来实现权限控制。另外,在代码中实现权限控制也比较简单,即使用PrincipalPermissionAttribute。
还是让我们开始看看demo吧。
首先然我们定义一个ServiceContract:
[ServiceContract] public interface ITestService { [OperationContract] string GetData(int value); }
我们给这个ITestService一个简单的实现:
public class TestService : ITestService { public string GetData(int value) { return string.Format("You entered: {0}", value); } }
接下来,让我们提供配置信息:
1)Binding配置
<wsHttpBinding> <binding name="wsHttpBinding1"> <security mode="TransportWithMessageCredential"> <transport clientCredentialType="None" realm="" /> <message clientCredentialType="UserName" /> </security> </binding> </wsHttpBinding>
2)Service配置
<service name="TestService.TestService" behaviorConfiguration="securityBehavior"> <endpoint address="" binding="wsHttpBinding" bindingConfiguration ="wsHttpBinding1" contract ="TestBase.ITestService" name ="testService" /> <endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" /> </service>
3)MembershipProvider及RoleProvider配置
<system.web> <roleManager enabled="true" defaultProvider ="MyProvider"> <providers> <add name="MyRoleProvider" type="TestBase.MyRoleProvider,TestBase"/> </providers> </roleManager> <membership> <providers> <add name="MyMembershipProvider" type="TestBase.MyMembershipProvider,TestBase"/> </providers> </membership> </system.web>
4)Behavior配置
<serviceBehaviors> <behavior name="securityBehavior"> <serviceCredentials> <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="MyMembershipProvider"/> </serviceCredentials> <serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="MyRoleProvider" /> </behavior> </serviceBehaviors>
好了,需要的类及配置都已准备好了,然后在IIS中,确保目标Web Site中包含https绑定,并且https绑定指向一个证书。在该Web Site中建立一个Application: TestService。浏览https://localhost/TestService/TestService.svc,可以正常浏览。
接下来,实现客户端调用代码:
try { TestServiceReference.TestServiceClient testServiceClient = new TestServiceReference.TestServiceClient ("testService"); testServiceClient .ClientCredentials.UserName.UserName = "userName"; testServiceClient .ClientCredentials.UserName.Password = "password"; ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => { return true; }; string result = testServiceClient .GetData(3456); Console.WriteLine("result : {0}", result); } catch (MessageSecurityException ex) { //authenticated failed Console.WriteLine(ex.Message); } catch(SecurityAccessDeniedException ex) { //authorized failed Console.WriteLine(ex.Message); } catch(FaultException ex) { Console.WriteLine(ex.Message); } catch (Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine(ex.StackTrace); } Console.ReadLine();
运行,服务可以正常访问。但是权限控制还没有加到服务端代码上,在TestService的GetData方法上加上PrincipalPermissionAttribute:
public class TestService : ITestService { [PrincipalPermission(SecurityAction.Demand, Role = "getData")] public string GetData(int value) { return string.Format("You entered: {0}", value); } }
此外,MyMembershipProvider以及MyRoleProvider的实现,在此就不提供了。在客户端调用代码中,通过提供错误的账户或密码,有getData权限的用户,以及无getData权限的用户,该方案将能够得到验证。
此外,PrincipalPermissionAttribute可以放到TestService.GetData上,也可以放到OperationContract的内部实现逻辑的类或方法上,那么在客户端异常处理是有所区别的。其中,MembershipProvider验证不通过,客户端将可以捕获到MessageSecurityException,而RoleProvider权限验证不通过,客户端将捕获到SecurityAccessDeniedException。如果你在你的MembershipProvider以及RoleProvider中定义自己的业务异常类,那么这些异常被抛到OperationContract方法中没有被处理的话,将会封装到FaultException抛到客户端。
通过MembershipProvider+RoleProvider+PrincipalPermissionAttribute,从而可以实现权限控制对具体代码的低依赖。而且验证和授权可以根据自己的需要很容易做出调整。代码中使用PrincipalPermissionAttribute这种declarative的方式,实现起来比较简洁,对已有业务代码的侵入性比较低。这种简洁的验证及授权方式,实现成本不高,而收到的效果却相当明显。
作者: Bright Zhang 发表于 2011-05-28 00:17 原文链接
推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架