关于WEB Service&WCF&WebApi实现身份验证之WCF篇(2)

本文介绍了六种WCF服务的身份验证方法,包括SOAPHeader验证、集成Windows用户组授权与认证及集成Windows验证等,详细展示了每种方法的实现步骤与配置。

因前段时间工作变动(换了新工作)及工作较忙暂时中断了该系列文章,今天难得有点空闲时间,就继续总结WCF身份验证的其它方法。前面总结了三种方法(详见:关于WEB Service&WCF&WebApi实现身份验证之WCF篇(1)),今天又将分享三种方法,完成WCF篇。

第四种:SOAP Header验证

首先定义一个WCF服务契约及服务实现类(后面的各种验证均采用该WCF服务),我这里直接采用默认的代码,如下:

服务契约定义:

服务实现Service1.svc:

 然后就开始编写实现SOAP Header验证的具体逻辑,为了便于扩展及可移植性,我这里新建一个类库项目:WcfServiceBehaviorExtension,在该项目中定义三个类,第一个是实现自IClientMessageInspector的类:AttachUserNamePasswordInspector,用于实现客户端在调用WCF服务时自动附加用户信息(用户名及密码);第二个是实现自IDispatchMessageInspector, IEndpointBehavior的类:ValidateUserNamePasswordBehavior,用于实现自定义消息分发及验证用户信息;第三个是继承自BehaviorExtensionElement的类:ValidateUserNamePasswordElement,用于实现endpointBehavior扩展,可直接配置在config文件中,若采用在代码动态添加ValidateUserNamePasswordBehavior则这类可以不用定义,下面依次分享这三个类的实现代码。

AttachUserNamePasswordInspector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace  WcfServiceBehaviorExtension
{
     public  class  AttachUserNamePasswordInspector : IClientMessageInspector
     {
         private  static  string  userName = System.Configuration.ConfigurationManager.AppSettings[ "username" ];
         private  static  string  password = System.Configuration.ConfigurationManager.AppSettings[ "pwd" ];
 
         public  void  AfterReceiveReply( ref  System.ServiceModel.Channels.Message reply,  object  correlationState)
         {
             
         }
 
         public  object  BeforeSendRequest( ref  System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
         {
             MessageHeader userNameHeader = MessageHeader.CreateHeader( "OperationUserName" "http://www.zuowenjun.cn" , userName,  false "" );
             MessageHeader pwdNameHeader = MessageHeader.CreateHeader( "OperationPwd" "http://www.zuowenjun.cn" , password,  false "" );
 
             request.Headers.Add(userNameHeader);
             request.Headers.Add(pwdNameHeader);
             return  null ;
         }
     }
}

ValidateUserNamePasswordBehavior

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
namespace  WcfServiceBehaviorExtension
{
 
     public  class  ValidateUserNamePasswordBehavior : IDispatchMessageInspector, IEndpointBehavior
     {
 
         #region IDispatchMessageInspector 成员
 
 
         private  string  GetHeaderValue( string  key)
         {
 
             int  index = OperationContext.Current.IncomingMessageHeaders.FindHeader(key,  "http://www.zuowenjun.cn" );
 
             if  (index >= 0)
             {
                 return  OperationContext.Current.IncomingMessageHeaders.GetHeader< string >(index).ToString();
             }
 
             return  null ;
         }
 
 
 
         public  object  AfterReceiveRequest( ref  System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
         {
             string  username = GetHeaderValue( "OperationUserName" );
             string  pwd = GetHeaderValue( "OperationPwd" );
 
             if  (!(username ==  "admin"  && pwd ==  "wcf.admin" ))
             {
                 throw  new  FaultException( "操作的用户名或密码不正确!" );
             }
             return  null ;
 
         }
 
 
 
         public  void  BeforeSendReply( ref  System.ServiceModel.Channels.Message reply,  object  correlationState)
         {
 
         }
 
 
 
         #endregion
 
 
 
         #region IEndpointBehavior 成员
 
 
 
         public  void  AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
         {
 
         }
 
 
 
         public  void  ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
         {
             clientRuntime.MessageInspectors.Add( new  AttachUserNamePasswordInspector());
         }
 
 
 
         public  void  ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
         {
             endpointDispatcher.DispatchRuntime.MessageInspectors.Add( new  ValidateUserNamePasswordBehavior());
         }
 
 
 
         public  void  Validate(ServiceEndpoint endpoint)
         {
         }
 
 
 
         #endregion
 
     }
 
}

ValidateUserNamePasswordElement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace  WcfServiceBehaviorExtension
{
     public  class  ValidateUserNamePasswordElement: BehaviorExtensionElement
     {
         public  override  Type BehaviorType
         {
             get  return  typeof (ValidateUserNamePasswordBehavior); }
         }
 
         protected  override  object  CreateBehavior()
         {
             return  new  ValidateUserNamePasswordBehavior();
         }
     }
}

定义好上述类后,我们再在WCF服务项目:WcfService1中引用该项目,然后修改配置文件web.config:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
< system.serviceModel >
   < extensions >
     < behaviorExtensions >
       < add  name="soapHaderValidation" type="WcfServiceBehaviorExtension.ValidateUserNamePasswordElement,WcfServiceBehaviorExtension"/>
     </ behaviorExtensions >
   </ extensions >
   < behaviors >
     < serviceBehaviors >
       < behavior >
         < serviceMetadata  httpGetEnabled="true" httpsGetEnabled="true"/>
         < serviceDebug  includeExceptionDetailInFaults="false"/>
       </ behavior >
     </ serviceBehaviors >
     < endpointBehaviors >
       < behavior  name="service1behavior">
         < soapHaderValidation  />
       </ behavior >
     </ endpointBehaviors >
   </ behaviors >
   < serviceHostingEnvironment  aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
   < services >
     < service  name="WcfService1.Service1">
       < endpoint  binding="wsHttpBinding" contract="WcfService1.IService1" behaviorConfiguration="service1behavior"></ endpoint >
     </ service >
   </ services >
</ system.serviceModel >

注意以下几点:

1.配置extensions > behaviorExtensions节点,里面添加ValidateUserNamePasswordElement类型信息,name为自定义名称,type为ValidateUserNamePasswordElement的完整类型名称,中间加逗号分隔,后面是该类所在的程序集名称;

2.配置endpointBehaviors > behavior节点,增加1中命名的节点soapHaderValidation,这里可能会报错误,但请不要理会,因为该节点在运行时才会生效的。

3.配置endpoint节点,需要添加behaviorConfiguration属性,并指定到2中所相对应的behavior

上述配置说白了就是配置自定义的服务行为类,使用在运行时注入到WCF服务管道中。

 服务端设置好后并发布运行,最后就是客户端引用该WCF服务,同时需要引用WcfServiceBehaviorExtension项目,然后修改配置文件app.config(我这里演示采用的是控制台程序):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
< system.serviceModel >
   < extensions >
     < behaviorExtensions >
       < add  name="soapHaderValidation" type="WcfServiceBehaviorExtension.ValidateUserNamePasswordElement,WcfServiceBehaviorExtension"/>
     </ behaviorExtensions >
   </ extensions >
   < behaviors >
     < endpointBehaviors >
       < behavior  name="service1behavior">
         < soapHaderValidation  />
       </ behavior >
     </ endpointBehaviors >
   </ behaviors >
     < bindings >
         < wsHttpBinding >
             < binding  name="WSHttpBinding_IService1" />
         </ wsHttpBinding >
     </ bindings >
     < client >
         < endpoint  address="http://localhost:7704/Service1.svc" binding="wsHttpBinding"
             bindingConfiguration="WSHttpBinding_IService1" contract="ServiceReference.IService1"
             name="WSHttpBinding_IService1" behaviorConfiguration="service1behavior">
         </ endpoint >
     </ client >
</ system.serviceModel >

注意事项与服务端配置文件相同,即需要配置extensions > behaviorExtensions节点、endpointBehaviors > behavior节点、endpoint节点添加behaviorConfiguration属性

另还需在appSettings节点中增加配置用户名及密码

配置好后就可以在程序中调用WCF服务,调用方法过程与普通的调用方法相同,并无其它区别。当然如果不想在客户端配置自定义节点及,可以通过以下代码来动态的添加用户名及密码信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static  void  Main( string [] args)
{
 
     try
     {
         var  client =  new  ServiceReference.Service1Client();
         using  (OperationContextScope contextScope =  new  OperationContextScope(client.InnerChannel))
         {
             AppendUserInfoHeader( "admin" "wcf.admin" );
             string  str = client.GetData(1);
             Console.WriteLine(str);
         }
     }
     catch  (Exception ex)
     {
         Console.WriteLine( "ERROR:{0}" , ex.Message);
     }
 
     Console.WriteLine( "我是主线程!" );
     Console.Read();
}
 
 
static  void  AppendUserInfoHeader( string  username,  string  pwd)
{
     MessageHeader header = MessageHeader.CreateHeader( "OperationUserName" "http://www.zuowenjun.cn" , username);
     OperationContext.Current.OutgoingMessageHeaders.Add(header);
 
     header = MessageHeader.CreateHeader( "OperationPwd" "http://www.zuowenjun.cn" , pwd);
     OperationContext.Current.OutgoingMessageHeaders.Add(header);
 
}

第五种:集成Windows用户组授权与认证

首先,在实现服务契约时,我们在需要授权与认证的类或方法中添加PrincipalPermission特性,代码如下:

1
2
3
4
5
[PrincipalPermission(SecurityAction.Demand,Role= "Users" )]
public  string  GetData( int  value)
{
     return  string .Format( "You entered: {0}" , value);
}

这里我配置的是只有当请求的WINDOWS凭证的用户角色类型为Users才允许调用该方法,可以设置成其它角色或指定用户名(Name)等。

然后修改配置文件,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
< system.serviceModel >
   < behaviors >
     < serviceBehaviors >
       < behavior  name="service1behavior">
         < serviceMetadata  httpGetEnabled="true" httpsGetEnabled="true"/>
         < serviceAuthorization  principalPermissionMode="UseWindowsGroups"></ serviceAuthorization >
       </ behavior >
     </ serviceBehaviors >
   </ behaviors >
   < serviceHostingEnvironment  aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
   < services >
     < service  name="WcfService1.Service1" behaviorConfiguration="service1behavior">
       < endpoint  name="service1" binding="wsHttpBinding" contract="WcfService1.IService1" ></ endpoint >
     </ service >
   </ services >
</ system.serviceModel >

这里需要注意,需配置serviceBehaviors > behavior > serviceAuthorization节点,将principalPermissionMode属性设为:UseWindowsGroups,并同时在service节点上指定behaviorConfiguration即可。

最后客户端正常引用该WCF服务,然后在调用WCF服务时,需要创建WindowsClientCredential对象,并指定用户名与密码,这里的用户名与密码均为该WCF服务端上服务器上的WINDOWS用户名及密码,具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static  void  Main( string [] args)
{
 
     try
     {
         var  client =  new  ServiceReference.Service1Client();
         NetworkCredential credential = client.ChannelFactory.Credentials.Windows.ClientCredential;
         credential.UserName =  "test" ;
         credential.Password =  "test" ;
 
         string  str = client.GetData(1);
         Console.WriteLine(str); ;
 
     }
     catch  (Exception ex)
     {
         Console.WriteLine( "ERROR:{0}" , ex.Message);
     }
 
     Console.WriteLine( "我是主线程!" );
     Console.Read();
}

第六种:集成Windows验证

服务定义与实现与文章开头贴出的代码相同,无任何特殊的地方,唯一的区别在于配置文件,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
< system.serviceModel >
   < behaviors >
     < serviceBehaviors >
       < behavior >
         < serviceMetadata  httpGetEnabled="true" httpsGetEnabled="true"/>
       </ behavior >
     </ serviceBehaviors >
   </ behaviors >
   < bindings >
     < basicHttpBinding >
       < binding  name="service1binding">
         < security  mode="TransportCredentialOnly">
           < transport  clientCredentialType="Windows"></ transport >
         </ security >
       </ binding >
     </ basicHttpBinding >
   </ bindings >
   < serviceHostingEnvironment  aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
   < services >
     < service  name="WcfService1.Service1" >
       < endpoint  name="service1" binding="basicHttpBinding" bindingConfiguration="service1binding" contract="WcfService1.IService1" ></ endpoint >
     </ service >
   </ services >
</ system.serviceModel >

注意事项:

1.绑定类型,我这里采用basicHttpBinding(不是每个绑定都支持WINDOWS验证的),同时需配置该绑定类型的security节点,mode设为TransportCredentialOnly,transport.clientCredentialType设为Windows。

完成上述配置后再直接访问WCF服务地址时,弹出需要输入用户名及密码,就表明WINDOWS验证已生效,接下来就是客户端引用该WCF服务,引用后配置文件自动进行了相应的更新,这里我们无需修改,然后在调用WCF服务时需要传入WindowsClientCredential对象,并指定用户名与密码,这里的用户名与密码均为该WCF服务端上服务器上的WINDOWS用户名及密码,与第五种相同代码就不贴出来了。

 

本文转自 梦在旅途 博客园博客,原文链接:http://www.cnblogs.com/zuowj/p/5078500.html  ,如需转载请自行联系原作者

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值