民工皇帝

龙在沙滩被虾戏,虎落平阳被犬欺. 虎伏深山听风啸,龙卧浅滩等海潮. 海到尽头天做岸,山登绝顶我为峰. 如日东山能在起,大鹏展翅恨天低。 谁无虎落平阳日,待我东山再起时. 有朝一日龙得水,必令长江水倒流. 有朝一日凤回巢,必让长城永不倒. 有朝一日虎归山,必要血染半边天. 有朝一日狮入林,我要气吼山河震. 有朝一日游地府,我让地府底朝天. 有朝一日游天边,众神跪在我身边. 有朝一日凤翔天,我要天下尽我鸣. 有朝一日我出头,我要天下唯我尊. 天下英雄出我辈,一入江湖岁月摧. 宏图霸业谈笑中,不胜人生一场醉

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

最近搞个项目,需要用到WCF来构建服务层,所以考虑到WCF的安全认证了。

WCF的身份认证模式分为

     1. 无身份验证:         所有的调用者都能访问服务

     2.Windows身份验证:Kerberos(有域服务器)和NTLM(工作组)

     3.用户名与密码:       客户端向服务端发送一个用户名和密码,服务端通过一些凭证库来验证

     4.X509证书:           客户端包含一个证书,服务端预先知道这些证书,当客户端发送请求时,服务端会验证客户端的证书

     5.定制机制:            允许开发者任意使用一种身份验证机制

     6.发布口令:             调用者与服务同事依赖一个安全口令

在这里我只描述一下通过MembershipProvider进行用户名/密码的认证

①先建立契约类库Contract具体代码如下

 

View Code
 1 using System;
 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接口

 

View Code
 1 using System;
 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控制台程序

 

View Code
 1 using System;
 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。

 

View Code
 1 <?xml version="1.0" encoding="utf-8" ?>
 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"/>
                //Membership
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文件自动配置完成。

然后可以调用了。具体代码如下

View Code
 1 using System;
 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(103).ToString());
19             Console.Read();
20         }
21     }
22 }

客户端代码完成,客户端config是在添加引用服务的时候自动添加成功的。不需要配置的。

现在可以运行,客户端程序看看有什么异常!不错,出现异常。这是因为客户端没有导入 服务器端生成的证书。

服务器端创建的FrankWCFServer这证书,需要导出来FrankWCFServer.cer,将导出来证书,安装在客户端PC上分别安装在当前用户的个人和受信任的根证书颁发机构里,然后在运行客户端程序一看OK了。 

在此声明一下,我是一个WCF的初学者,这次的配置是在服务器和客户端两台机器上做的配置,搞了2天,刚才调通,好多东西都不懂,还在学习中,这只是一个笔记,懒得记在本子上,希望看到的别拍砖就行,先行谢过。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2011-12-08 20:39  民工皇帝  阅读(994)  评论(1编辑  收藏  举报