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

简介:

因前段时间工作变动(换了新工作)及工作较忙暂时中断了该系列文章,今天难得有点空闲时间,就继续总结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  ,如需转载请自行联系原作者

相关文章
【Azure 应用服务】Web App Service 中的 应用程序配置(Application Setting) 怎么获取key vault中的值
【Azure 应用服务】Web App Service 中的 应用程序配置(Application Setting) 怎么获取key vault中的值
210 0
【Azure App Service】PowerShell脚本批量添加IP地址到Web App允许访问IP列表中
Web App取消公网访问后,只允许特定IP能访问Web App。需要写一下段PowerShell脚本,批量添加IP到Web App的允许访问IP列表里!
264 2
|
关系型数据库 MySQL Linux
【Azure 应用服务】在创建Web App Service的时候,选Linux系统后无法使用Mysql in App
【Azure 应用服务】在创建Web App Service的时候,选Linux系统后无法使用Mysql in App
145 0
【Azure 应用服务】在创建Web App Service的时候,选Linux系统后无法使用Mysql in App
|
缓存 JavaScript 前端开发
Web Workers与Service Workers:后台处理与离线缓存
Web Workers 和 Service Workers 是两种在Web开发中处理后台任务和离线缓存的重要技术。它们在工作原理和用途上有显著区别。
333 1
|
Shell PHP Windows
【Azure App Service】Web Job 报错 UNC paths are not supported. Defaulting to Windows directory.
【Azure App Service】Web Job 报错 UNC paths are not supported. Defaulting to Windows directory.
239 0
|
Linux 应用服务中间件 网络安全
【Azure 应用服务】查看App Service for Linux上部署PHP 7.4 和 8.0时,所使用的WEB服务器是什么?
【Azure 应用服务】查看App Service for Linux上部署PHP 7.4 和 8.0时,所使用的WEB服务器是什么?
159 0
【Azure 应用服务】通过 Web.config 开启 dotnet 应用的 stdoutLog 日志,查看App Service 产生500错误的原因
【Azure 应用服务】通过 Web.config 开启 dotnet 应用的 stdoutLog 日志,查看App Service 产生500错误的原因
334 0
|
Linux Python
【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https
【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https
299 0
|
存储 安全 网络安全
【Azure 环境】使用Azure中的App Service部署Web应用,以Windows为主机系统是否可以启动防病毒,防恶意软件服务呢(Microsoft Antimalware)?
【Azure 环境】使用Azure中的App Service部署Web应用,以Windows为主机系统是否可以启动防病毒,防恶意软件服务呢(Microsoft Antimalware)?
228 0
|
存储 Linux 网络安全
【Azure 应用服务】App Service For Linux 如何在 Web 应用实例上住抓取网络日志
【Azure 应用服务】App Service For Linux 如何在 Web 应用实例上住抓取网络日志
170 0