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