关于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  ,如需转载请自行联系原作者

相关文章
|
3月前
【Azure 应用服务】Web App Service 中的 应用程序配置(Application Setting) 怎么获取key vault中的值
【Azure 应用服务】Web App Service 中的 应用程序配置(Application Setting) 怎么获取key vault中的值
|
18天前
【Azure App Service】PowerShell脚本批量添加IP地址到Web App允许访问IP列表中
Web App取消公网访问后,只允许特定IP能访问Web App。需要写一下段PowerShell脚本,批量添加IP到Web App的允许访问IP列表里!
|
3月前
|
存储 JSON 数据安全/隐私保护
"FastAPI身份验证与授权的奥秘:如何用Python打造坚不可摧的Web应用,让你的项目一鸣惊人?"
【8月更文挑战第31天】在现代Web开发中,保证应用安全性至关重要,FastAPI作为高性能Python框架,提供了多种身份验证与授权方式,包括HTTP基础认证、OAuth2及JWT。本文将对比这些机制并附上示例代码,展示如何使用HTTP基础认证、OAuth2协议以及JWT进行用户身份验证,确保只有合法用户才能访问受保护资源。通过具体示例,读者可以了解如何在FastAPI项目中实施这些安全措施。
138 1
|
3月前
|
关系型数据库 MySQL Linux
【Azure 应用服务】在创建Web App Service的时候,选Linux系统后无法使用Mysql in App
【Azure 应用服务】在创建Web App Service的时候,选Linux系统后无法使用Mysql in App
【Azure 应用服务】在创建Web App Service的时候,选Linux系统后无法使用Mysql in App
|
3月前
|
JavaScript 安全 前端开发
Node.js身份验证全攻略:策略与实践,打造坚不可摧的Web应用安全防线!
【8月更文挑战第22天】Node.js作为强大的服务器端JavaScript平台,对于构建高效网络应用至关重要。本文探讨其身份验证策略,涵盖从基于token至复杂的OAuth 2.0及JWT。Passport.js作为认证中间件,支持本地账号验证及第三方服务如Google、Facebook登录。同时介绍JWT轻量级验证机制,确保数据安全传输。开发者可根据应用需求选择合适方案,注重安全性以保护用户数据。
69 1
|
3月前
|
开发框架 JSON .NET
ASP.NET Core 标识(Identity)框架系列(三):在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证
ASP.NET Core 标识(Identity)框架系列(三):在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证
|
3月前
|
存储 安全 测试技术
Web2py中的身份验证与授权之谜:如何让你的Web应用飞起来?
【8月更文挑战第31天】在Web开发中,确保应用安全性至关重要,Web2py作为Python Web框架,提供了强大的身份验证与授权功能。本文探讨了Web2py的身份验证(包括表单验证、密码加密及会话管理)与授权(涵盖角色权限、URL过滤及用户组管理)。通过示例代码展示了用户模型定义、角色关系设置及登录流程处理等关键步骤。合理利用这些功能并结合最佳实践如使用内置验证、灵活控制访问权限及编写测试,可帮助开发者高效构建安全稳定的应用程序。
20 0
|
3月前
|
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.
|
3月前
|
Linux 应用服务中间件 网络安全
【Azure 应用服务】查看App Service for Linux上部署PHP 7.4 和 8.0时,所使用的WEB服务器是什么?
【Azure 应用服务】查看App Service for Linux上部署PHP 7.4 和 8.0时,所使用的WEB服务器是什么?
|
3月前
【Azure 应用服务】通过 Web.config 开启 dotnet 应用的 stdoutLog 日志,查看App Service 产生500错误的原因
【Azure 应用服务】通过 Web.config 开启 dotnet 应用的 stdoutLog 日志,查看App Service 产生500错误的原因