最近搞个项目,需要用到WCF来构建服务层,所以考虑到WCF的安全认证了。
WCF的身份认证模式分为
1. 无身份验证: 所有的调用者都能访问服务
2.Windows身份验证:Kerberos(有域服务器)和NTLM(工作组)
3.用户名与密码: 客户端向服务端发送一个用户名和密码,服务端通过一些凭证库来验证
4.X509证书: 客户端包含一个证书,服务端预先知道这些证书,当客户端发送请求时,服务端会验证客户端的证书
5.定制机制: 允许开发者任意使用一种身份验证机制
6.发布口令: 调用者与服务同事依赖一个安全口令
在这里我只描述一下通过MembershipProvider进行用户名/密码的认证
①先建立契约类库Contract具体代码如下
2 using System.Collections.Generic;
3 using System.Text;
4 using System.ServiceModel;
5
6 namespace Contracts
7 {
8 [ServiceContract]
9 public interface IServiceHosting
10 {
11 /// <summary>
12 /// 加法
13 /// </summary>
14 /// <param name="x"></param>
15 /// <param name="y"></param>
16 /// <returns></returns>
17 [OperationContract]
18 double ADD(double x, double y);
19
20 /// <summary>
21 /// 减法
22 /// </summary>
23 /// <param name="x"></param>
24 /// <param name="y"></param>
25 /// <returns></returns>
26 [OperationContract]
27 double substruction(double x, double y);
28
29 /// <summary>
30 /// 乘法
31 /// </summary>
32 /// <param name="x"></param>
33 /// <param name="y"></param>
34 /// <returns></returns>
35 [OperationContract]
36 double multiplication(double x, double y);
37
38 /// <summary>
39 /// 除法
40 /// </summary>
41 /// <param name="x">被除数</param>
42 /// <param name="y">除数</param>
43 /// <returns></returns>
44 [OperationContract(Name = "division")]
45 double division(double x, double y);
46 }
47 }
第二步 建立 服务类,Service 继承,契约Contract类库里的IServiceHosting接口
2 using System.Collections.Generic;
3 using System.Text;
4 using Contracts;
5
6 namespace Service
7 {
8 public class ServiceHosting : IServiceHosting
9 {
10 public double ADD(double x, double y)
11 {
12 return x + y;
13 }
14
15 public double substruction(double x, double y)
16 {
17 return x - y;
18 }
19
20 public double multiplication(double x, double y)
21 {
22 return x * y;
23 }
24
25 public double division(double x, double y)
26 {
27 if (y == 0)
28 {
29 throw new Exception("除数不能为零");
30 }
31 else
32 {
33 return x / y;
34 }
35 }
36 }
37 }
第三步建立服务主机Hosting控制台程序
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Web.Security;
5 using System.ServiceModel;
6 using Contracts;
7 using Service;
8
9 namespace Hosting
10 {
11 class Program
12 {
13 static void Main(string[] args)
14 {
15 try
16 { //判断数据库里是否有Sytech这个用户,没有就添加一条
17 if (Membership.FindUsersByName("Sytech").Count == 0)
18 {
19 Membership.CreateUser("Sytech", "ASD!@#123", "Sytech@sytech.com.cn");
20 }
21 using (ServiceHost host = new ServiceHost(typeof(ServiceHosting)))
22 {
23 host.Opened += delegate
24 {
25 Console.WriteLine(host.BaseAddresses[0].ToString());
26 };
27 host.Open();
28 Console.WriteLine("Host start");
29 Console.Read();
30 }
31 }
32 catch (Exception ex)
33 {
34 Console.WriteLine(ex.Message);
35 Console.Read();
36 }
37 }
38 }
39 }
在这里说明一下啊,这次是通过MembershipProvider来进行密码/用户名的认证,所以需要先生成aspnetdb数据库,这个大家都知道怎么来搞,不知道的可以去baidu查一下。创建出来的数据表可以同时服务于多个应用,所有每一个表中都具有一个名称为ApplicationId的字段来明确该条记录对应的应用。而所有应用记录维护在aspnet_Applications这么一个表中。现在我们需要通过执行下面一段SQL脚本在该表中添加一条表示我们应用的记录
1: INSERT INTO [aspnet_Applications]
2: ([ApplicationName]
3: ,[LoweredApplicationName]
4: ,[ApplicationId]
5: ,[Description])
6: VALUES
7: (
8: 'Demo'
9: ,'demo'
10: ,NEWID()
11: ,''
12: )
经过这三步,就可以建立服务器端的开发就完成,现在我们来配置服务器端。
服务器端的配置主要是Config的配置,在配置前我们先在服务器上生成服务证书,
制作证书 makecert -r -pe -n "CN=FrankWCFServer" -ss My -sky exchange 通过这个来生成证书(默认是在currentuser,Config里配置就是storeLocation="CurrentUser",如果使用makecert -r -pe -n "CN=FrankWCFServer" -ss My -sky exchange -sr LocalMachine,那么证书就存放在本地计算机的个人里 Config里配置就是storeLocation="LocalMachine"),证书生成后可以在mmc里,添加/或删除管理单元-》证书-添加-》当前用户-》确定,就可以在 个人和受信任的根证书颁发机构 里找到你创建的FrankWCFServer证书。下来配置服务器端的Config。
2 <configuration>
3 <connectionStrings>
4 <add name="aspnetdb" connectionString="Server=.; Database=aspnetdb; Uid=sa; Pwd=123456"/>
5 </connectionStrings>
6 <system.web>
7 <membership defaultProvider="myProvider">
8 <providers>//Membership的配置 整个项目使用的.netFramwork3.0
9 <add name="myProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
10 connectionStringName="aspnetdb" applicationName="MembershipAuthenticationDemo" requiresQuestionAndAnswer="false" />
11 </providers>
12 </membership>
13 </system.web>
14 <system.serviceModel>
15 <services>
16 <service name="Service.ServiceHosting" behaviorConfiguration="HostBehaviors">
17 <endpoint address="" binding="wsHttpBinding" bindingConfiguration="userNameCredentialBinding" contract="Contracts.IServiceHosting" />
18 <host>
19 <baseAddresses>//将Contract和Service 在800端口下发布
20 <add baseAddress="http://win-opvvg1v8458:800/ServiceHosting"/>
21 </baseAddresses>
22 </host>
23 </service>
24 </services>
25 <behaviors>
26 <serviceBehaviors>
27 <behavior name="HostBehaviors">
28 <serviceCredentials>
29 <serviceCertificate storeLocation="CurrentUser" storeName="My" x509FindType="FindBySubjectName" findValue="FrankWCFServer"/>
30 <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="myProvider"/>
31 </serviceCredentials>
32 <serviceMetadata httpGetEnabled="true"/>
33 </behavior>
34 </serviceBehaviors>
35 </behaviors>
36 <bindings>
37 <wsHttpBinding>
38 <binding name="userNameCredentialBinding">
39 <security mode="Message">
40 <message clientCredentialType="UserName"/>
41 </security>
42 </binding>
43 </wsHttpBinding>
44 </bindings>
45 </system.serviceModel>
46 </configuration>
•配置名称为AspNetDb的连接字符串连接的是我们刚刚创建的数据库,并通过aspnet_regsql.exe工具在该数据库中创建了所需的数据库对象;
•表示Membership配置节的<system.web>/<membership>节点下配置了唯一的SqlMembershipProvider,配置名称为myProvider。上面配置的连接字符创名称AspNetDb配置在connectionStringName属性中,意味着该SqlMembershipProvider会将我们创建的数据库作为用户帐号存储;
•服务终结点采用WSHttpBinding,采用Message安全模式,客户端凭证类型被设置为UserName;
•服务应用了一个配置名称为membershipAuthentication的服务行为,该行为中通过<serviceCertificate>节点设置了服务证书。在表示用户名/密码认证配置的<userNameAuthentication>节点中,将认证模式设置成MembershipProvider,而membershipProviderName属性的值为我们在<system.web>/<membership>中设置的MembershipProvider的名称。
到目前为止,在我们创建的数据库中并没有用户帐户记录。为了演示认证的效果,我们必须创建相关用户帐户记录。为了省事,我直接将相关的代码写在了服务寄宿的代码中。如下面的代码片断所示,在对服务进行寄宿之前,我通过调用Membership的静态方法CreateUser创建了一个用户名、密码和Email分别为"Sytech", "ASD!@#123", Sytech@sytech.com.cn。这就是我们为什么在Hosting里开始就写
if (Membership.FindUsersByName("Sytech").Count == 0)
{
Membership.CreateUser("Sytech", "ASD!@#123", "Sytech@sytech.com.cn");
}
现在服务器端就可以告一段落。我们来启动Hosting之后可以在浏览器里输入http://win-opvvg1v8458:800/ServiceHosting可以看到如下的页面。
服务器端配置成功。
客户端的调用,在这里建一个控制台应用程序Client,添加服务引用,在地址里输入http://win-opvvg1v8458:800/ServiceHosting点击确定。引用成功Client的Config文件自动配置完成。
然后可以调用了。具体代码如下
2 using System.Collections.Generic;
3 using System.Text;
4 using System.ServiceModel.Security;
5 using System.ServiceModel;
6 using Contracts;
7
8 namespace Client
9 {
10 class Program
11 {
12 static void Main(string[] args)
13 {
14 Ssss.ServiceHostingClient client = new Ssss.ServiceHostingClient();
15 UserNamePasswordClientCredential uncc = client.ClientCredentials.UserName;
16 uncc.UserName = "Sytech";
17 uncc.Password = "ASD!@#123";
18 Console.WriteLine(client.ADD(10, 3).ToString());
19 Console.Read();
20 }
21 }
22 }
客户端代码完成,客户端config是在添加引用服务的时候自动添加成功的。不需要配置的。
现在可以运行,客户端程序看看有什么异常!不错,出现异常。这是因为客户端没有导入 服务器端生成的证书。
在服务器端创建的FrankWCFServer这证书,需要导出来FrankWCFServer.cer,将导出来证书,安装在客户端PC上分别安装在当前用户的个人和受信任的根证书颁发机构里,然后在运行客户端程序一看OK了。
在此声明一下,我是一个WCF的初学者,这次的配置是在服务器和客户端两台机器上做的配置,搞了2天,刚才调通,好多东西都不懂,还在学习中,这只是一个笔记,懒得记在本子上,希望看到的别拍砖就行,先行谢过。