写在前面:在前面的两篇文章我们写了在WCF中使用userName/password和x509证书的验证方式
http://www.cnblogs.com/liujiang/archive/2008/11/24/1338952.html x509
http://www.cnblogs.com/liujiang/archive/2008/11/21/1338384.html userName/password
1. Transfer Security
Transfer Security 主要包括三个方面: "消息完整性(Message Integrity)"、"消息机密性 (Message Confidentiality)" 和 "交互验证(Mutual Authentication)"。
消息完整性必须确保消息在传输过程中没有被篡改,接收的消息是完整且正确的;消息机密性必须确保消息不会被任何第三方查阅,消息内容不会泄漏给任何非相关人员;而交互认证则是指客户端和服务器必须通过某种信任机制才能建立正确的连接,同时交互认证还要监测并阻止拒绝服务攻击(DOS)。通常的做法是对消息进行数字签名来确保其完整性,使用非对称加密算法来阻止消息内容外泄,而用户名/密码、X.509 数字证书等方式则可以用来验证对方身份,在这里我们主要讲述如何在WCF中使用Membership provider的身份验证方式.
2.使用 ASP.NET Membership Provider
ASP.NET Membership Provider是一种功能,可供 ASP.NET 开发人员用于创建允许用户创建唯一用户名和密码组合的网站。使用此工具,任何用户都可以在该网站上建立帐户,并登录网站以便独占访问该网站及其服务。这与要求用户在 Windows 域中具有帐户的 Windows 安全完全不同。所有提供凭据(用户名/密码组合)的用户都可以使用该网站及其服务。在做这个Demo之前,我们需要创建本地的aspnetdb数据,使用vs2008的tool中的 command命令下执行:aspnet_regsql。这个命令会为我们本地创建一个名为aspnetdb的数据。同时我们使用ASP.Net configuration工具创建1个角色:角色名为:super user.建立一个用户;user1.这个user1的角色是super user.具体关于membership provider方面的知识可以在MSDN是查找相关的知识,在这里我们就不多说,事实上在WCF中,使用ASP.NET Membership Provider 和使用userName/password一样的机制,同样我们也需要在服务端装上x509的证书。用以在传输过程中用于加密用户名和密码。
创建证书:makecert -r -pe -n "CN=MyServer" -ss My -sky exchange .
3.Solution 结构如下图:
solution:WCFMembershipClient WCF client端 Console Application.
WCFMembershipServices WCF Application
Windows Communication Foundation (WCF) 开发人员可以出于安全目的利用这些功能。当集成到 WCF 应用程序中时,用户必须向 WCF 客户端应用程序提供用户名/密码组合。若要将数据传输到 WCF 服务,请使用支持用户名/密码凭据的绑定,如 WSHttpBinding 并将客户端凭据类型设置为 UserName。在服务上,WCF 安全基于用户名和密码对用户进行身份验证,还会分配由 ASP.NET 角色指定的角色。
配置成员资格提供程序,加在服务端配置文件中
<connectionStrings>
<add name="SqlConn" connectionString="Data Source=localhost;Integrated Security=true;database=aspnetdb;"/>
</connectionStrings>
<system.web>
<!-- Configure the Sql Membership Provider -->
<membership defaultProvider="SqlMembershipProvider" userIsOnlineTimeWindow="15">
<providers>
<clear/>
<add name="SqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="SqlConn" applicationName="MembershipAndRoleProviderSample" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" passwordFormat="Hashed"/>
</providers>
</membership>
<!-- Configure the Sql Role Provider -->
<roleManager enabled="true" defaultProvider="SqlRoleProvider">
<providers>
<add name="SqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="SqlConn" applicationName="WCFMembership"/>
</providers>
</roleManager>
<compilation debug="true"/>
</system.web>
创建服务
[ServiceContract]
public interface IService1
{
[OperationContract]
bool test();
}
public class Service1 : IService1//实现服务
{
public Service1()
{
}
#region IService1 Members
[PrincipalPermission(SecurityAction.Demand,Role="super user")]
public bool test()
{
return true;
}
#endregion
}
服务端的配置文件如下:
<system.serviceModel>
<services>
<service name="WCFMembershipServices.Service1" behaviorConfiguration="MembershipBehavior">
<!-- use base address provided by host -->
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="Binding1" contract="WCFMembershipServices.IService1"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<!-- Set up a binding that uses Username as the client credential type -->
<binding name="Binding1">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="MembershipBehavior">
<!-- Configure role based authorization to use the Role Provider -->
<serviceAuthorization principalPermissionMode="None" roleProviderName="SqlRoleProvider"/>
<serviceCredentials>
<!-- Configure user name authentication to use the Membership Provider -->
//将 membershipProviderName 属性设置为提供程序的名称将 membershipProviderName 属性设置为提供程序的名称
<userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="SqlMembershipProvider"/>
<!-- Configure the service certificate -->
<serviceCertificate storeLocation="CurrentUser" storeName="My" x509FindType="FindBySubjectName" findValue="MyServer"/>
</serviceCredentials>
<!--For debugging purposes set the includeExceptionDetailInFaults attribute to true-->
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
4. 创建客户端
<system.serviceModel>
<client>
<endpoint name=""
address="http://localhost:51991/Service1.svc"
behaviorConfiguration ="ClientBehavior"
binding="wsHttpBinding"
bindingConfiguration="Binding1"
contract="IService1" >
<identity>
<certificate encodedValue="AwAAAAEAAAAUAAAAqxw7daba6mcItJ/tKIAFZfz" </identity>
</endpoint>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="ClientBehavior">
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="None" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<!-- Set up a binding that uses Username as the client credential type -->
<binding name="Binding1">
<security mode ="Message">
<message clientCredentialType ="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
客户端代理由Svcutil工具生成的,在这里就不贴出来了。在vs2008的tool command中可以svcutil +‘元数据地址’就为帮你生成的。
Service1Client client = new Service1Client();
client.ClientCredentials.UserName.UserName = "user1";
client.ClientCredentials.UserName.Password = "@abc123";
client.test();
这样就可以成功能调用了:如果能成功能过验证的话