博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WCF权限认证多种方式
阅读量:5130 次
发布时间:2019-06-13

本文共 14151 字,大约阅读时间需要 47 分钟。

WCF身份验证一般常见的方式有:自定义用户名及密码验证、X509证书验证、ASP.NET成员资格(membership)验证、SOAP Header验证、Windows集成验证、WCF身份验证服务(AuthenticationService),这些验证方式其实网上都有相关的介绍文章,我这里算是一个总结吧,顺便对于一些注意细节进行说明,以便大家能更好的掌握这些知识。

第一种:自定义用户名及密码验证(需要借助X509证书)

由于该验证需要借助于X509证书,所以我们需要先创建一个证书,可以利用MS自带的makecert.exe程序来制作测试用证书,使用步骤:请依次打开开始->Microsoft Visual Studio 2010(VS菜单,版本不同,名称有所不同)->Visual Studio Tools->Visual Studio 命令提示,然后执行以下命令:

makecert -r -pe -n "CN=ZwjCert" -ss TrustedPeople -sr LocalMachine -sky exchange

上述命令中除了我标粗的部份可改成你实际的请求外(为证书名称),其余的均可以保持不变,命令的意思是:创建一个名为ZwjCert的证书将将其加入到本地计算机的受信任人区域中。

如果需要查看该证书,那么可以通过MMC控制台查询证书,具体操作步骤如下:

运行->MMC,第一次打开Windows没有给我们准备好直接的管理证书的入口,需要自行添加,添加方法如下:

1. 在控制台菜单,文件→添加/删除管理单元→添加按钮→选”证书”→添加→选”我的用户账户”→关闭→确定

2. 在控制台菜单,文件→添加/删除管理单元→添加按钮→选”证书”→添加→选”计算机账户”→关闭→确定

这样MMC中左边就有菜单了,然后依次展开:证书(本地计算机)->受信任人->证书,最后就可以在右边的证书列表中看到自己的证书了,如下图示:

 

证书创建好,我们就可以开始编码了,本文主要讲的就是WCF,所以我们首先定义一个WCF服务契约及服务实现类(后面的各种验证均采用该WCF服务),我这里直接采用默认的代码,如下:

要实现用户名及密码验证,就需要定义一个继承自UserNamePasswordValidator的用户名及密码验证器类CustomUserNameValidator,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
namespace 
WcfAuthentications
{
    
public 
class 
CustomUserNameValidator : UserNamePasswordValidator
    
{
 
        
public 
override 
void 
Validate(
string 
userName, 
string 
password)
        
{
            
if 
(
null 
== userName || 
null 
== password)
            
{
                
throw 
new 
ArgumentNullException();
            
}
            
if 
(userName != 
"admin" 
&& password != 
"wcf.admin"
//这里可依实际情况下实现用户名及密码判断
            
{
                
throw 
new 
System.IdentityModel.Tokens.SecurityTokenException(
"Unknown Username or Password"
);
            
}
 
        
}
    
}
}

代码很简单,只是重写其Validate方法,下面就是将创建WCF宿主,我这里采用控制台程序

代码部份:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace 
WcfHost
{
    
class 
Program
    
{
        
static 
void 
Main(
string
[] args)
        
{
            
using 
(
var 
host = 
new 
ServiceHost(
typeof
(Service1)))
            
{
                
host.Opened += 
delegate
                
{
                    
Console.WriteLine(
"Service1 Host已开启!"
);
                
};
                
host.Open();
                
Console.ReadKey();
            
}
        
}
    
}
}

APP.CONFIG部份(这是重点,可以使用WCF配置工具来进行可视化操作配置,参见:http://www.cnblogs.com/Moosdau/archive/2011/04/17/2019002.html):

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
<
system.serviceModel
>
  
<
bindings
>
    
<
wsHttpBinding
>
      
<
binding 
name="Service1Binding">
        
<
security 
mode="Message">
          
<
message 
clientCredentialType="UserName" />
        
</
security
>
      
</
binding
>
    
</
wsHttpBinding
>
  
</
bindings
>
  
<
services
>
    
<
service 
behaviorConfiguration="Service1Behavior" name="WcfAuthentications.Service1">
      
<
endpoint 
address="" binding="wsHttpBinding" bindingConfiguration="Service1Binding"
        
contract="WcfAuthentications.IService1">
        
<
identity
>
          
<
dns 
value="ZwjCert" />
        
</
identity
>
      
</
endpoint
>
      
<
endpoint 
address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      
<
host
>
        
<
baseAddresses
>
          
<
add 
baseAddress="http://localhost:8732/WcfAuthentications/Service1/" />
        
</
baseAddresses
>
      
</
host
>
    
</
service
>
  
</
services
>
  
<
behaviors
>
    
<
serviceBehaviors
>
      
<
behavior 
name="Service1Behavior">
        
<
serviceMetadata 
httpGetEnabled="true" />
        
<
serviceDebug 
includeExceptionDetailInFaults="false" />
        
<
serviceCredentials
>
          
<
serviceCertificate 
findValue="ZwjCert" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="TrustedPeople" />
          
<
userNameAuthentication 
userNamePasswordValidationMode="Custom"
            
customUserNamePasswordValidatorType="WcfAuthentications.CustomUserNameValidator,WcfAuthentications" />
        
</
serviceCredentials
>
      
</
behavior
>
    
</
serviceBehaviors
>
  
</
behaviors
>
</
system.serviceModel
>

这里面有几个需要注意的点:

1.<dns value="ZwjCert" />与<serviceCertificate findValue="ZwjCert" ..>中的value必需都为证书的名称,即:ZwjCert;

2.Binding节点中需配置security节点,message子节点中的clientCredentialType必需设为:UserName;

3.serviceBehavior节点中,需配置serviceCredentials子节点,其中serviceCertificate 中各属性均需与证书相匹配,userNameAuthentication的userNamePasswordValidationMode必需为Custom,customUserNamePasswordValidatorType为上面自定义的用户名及密码验证器类的类型及其程序集

最后就是在客户端使用了,先引用服务,然后看下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
<
system.serviceModel
>
    
<
bindings
>
        
<
wsHttpBinding
>
            
<
binding 
name="WSHttpBinding_IService1" >
                
<
security 
mode="Message">
                    
<
transport 
clientCredentialType="Windows" proxyCredentialType="None"
                        
realm="" />
                    
<
message 
clientCredentialType="UserName" negotiateServiceCredential="true"
                        
algorithmSuite="Default" />
                
</
security
>
            
</
binding
>
        
</
wsHttpBinding
>
    
</
bindings
>
    
<
client
>
        
<
endpoint 
address="http://localhost:8732/WcfAuthentications/Service1/"
            
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
            
contract="ServiceReference1.IService1" name="WSHttpBinding_IService1">
            
<
identity
>
                
<
dns 
value="ZwjCert" />
            
</
identity
>
        
</
endpoint
>
    
</
client
>
</
system.serviceModel
>

为了突出重点,我这里对Binding节点进行了精简,去掉了许多的属性配置,仅保留重要的部份,如:security节点,修改其endpoint下面的identity中<dns value="ZwjCert" />,这里的value与服务中所说的相同节点相同,就是证书名称,如果不相同,那么就会报错,具体的错误消息大家可以自行试下,我这里限于篇幅内容就不贴出来了。

客户端使用服务代码如下:

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
namespace 
WCFClient
{
    
class 
Program
    
{
        
static 
void 
Main(
string
[] args)
        
{
            
using 
(
var 
proxy = 
new 
ServiceReference1.Service1Client())
            
{
                
proxy.ClientCredentials.UserName.UserName = 
"admin"
;
                
proxy.ClientCredentials.UserName.Password = 
"wcf.admin"
;
                
string 
result = proxy.GetData(1);
                
Console.WriteLine(result);
                
var 
compositeObj = proxy.GetDataUsingDataContract(
new 
CompositeType() { BoolValue = 
true
, StringValue = 
"test" 
});
                
Console.WriteLine(SerializerToJson(compositeObj));
            
}
            
Console.ReadKey();
        
}
 
        
/// <summary>
        
/// 序列化成JSON字符串
        
/// </summary>
        
static 
string 
SerializerToJson<T>(T obj) 
where 
T:
class
        
{
            
var 
serializer = 
new 
DataContractJsonSerializer(
typeof
(T));
            
var 
stream = 
new 
MemoryStream();
            
serializer.WriteObject(stream,obj);
 
            
byte
[] dataBytes = 
new 
byte
[stream.Length];
 
            
stream.Position = 0;
            
stream.Read(dataBytes, 0, (
int
)stream.Length);
            
string 
dataString = Encoding.UTF8.GetString(dataBytes);
            
return 
dataString;
        
}
    
}
}

运行结果如下图示:

  

如果不传入用户名及密码或传入不正确的用户名及密码,均会报错:

第二种:X509证书验证

首先创建一个证书,我这里就用上面创建的一个证书:ZwjCert;由于服务器端及客户端均需要用到该证书,所以需要导出证书,在客户端的电脑上导入该证书,以便WCF可进行验证。

WCF服务契约及服务实现类与第一种方法相同,不再重贴代码。

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
33
34
35
36
37
38
<
system.serviceModel
>
  
<
bindings
>
    
<
wsHttpBinding
>
      
<
binding 
name="Service1Binding">
        
<
security 
mode="Message">
          
<
message 
clientCredentialType="Certificate" />
        
</
security
>
      
</
binding
>
    
</
wsHttpBinding
>
  
</
bindings
>
  
<
services
>
    
<
service 
behaviorConfiguration="Service1Behavior" name="WcfAuthentications.Service1">
      
<
endpoint 
address="" binding="wsHttpBinding" bindingConfiguration="Service1Binding"
        
contract="WcfAuthentications.IService1">
      
</
endpoint
>
      
<
endpoint 
address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      
<
host
>
        
<
baseAddresses
>
          
<
add 
baseAddress="http://127.0.0.1:8732/WcfAuthentications/Service1/" />
        
</
baseAddresses
>
      
</
host
>
    
</
service
>
  
</
services
>
  
<
behaviors
>
    
<
serviceBehaviors
>
      
<
behavior 
name="Service1Behavior">
        
<
serviceMetadata 
httpGetEnabled="true" />
        
<
serviceDebug 
includeExceptionDetailInFaults="false" />
        
<
serviceCredentials
>
          
<
serviceCertificate 
findValue="ZwjCert" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="TrustedPeople" />
          
<
clientCertificate
>
            
<
authentication 
certificateValidationMode="None"/>
          
</
clientCertificate
>
        
</
serviceCredentials
>
      
</
behavior
>
    
</
serviceBehaviors
>
  
</
behaviors
>
</
system.serviceModel
>

这里需注意如下几点:

1.<message clientCredentialType="Certificate" />clientCredentialType设为:Certificate;

2.需配置serviceCredentials节点,其中serviceCertificate 中各属性均需与证书相匹配,clientCertificate里面我将authentication.certificateValidationMode="None",不设置采用默认值其实也可以;

客户端引用服务,自动生成如下配置信息:

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
<
system.serviceModel
>
    
<
bindings
>
        
<
wsHttpBinding
>
            
<
binding 
name="WSHttpBinding_IService1">
                
<
security 
mode="Message">
                    
<
transport 
clientCredentialType="Windows" proxyCredentialType="None"
                        
realm="" />
                    
<
message 
clientCredentialType="Certificate" negotiateServiceCredential="true"
                        
algorithmSuite="Default" />
                
</
security
>
            
</
binding
>
        
</
wsHttpBinding
>
    
</
bindings
>
    
<
client
>
        
<
endpoint 
address="http://127.0.0.1:8732/WcfAuthentications/Service1/"
            
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
            
contract="ServiceReference1.IService1" name="WSHttpBinding_IService1" behaviorConfiguration="Service1Nehavior">
            
<
identity
>
                
<
certificate 
encodedValue="AwAAAAEAAAAUAAAAkk2avjNCItzUlS2+Xj66ZA2HBZYgAAAAAQAAAOwBAAAwggHoMIIBVaADAgECAhAIAOzFvLxLuUhHJRwHUUh9MAkGBSsOAwIdBQAwEjEQMA4GA1UEAxMHWndqQ2VydDAeFw0xNTEyMDUwMjUyMTRaFw0zOTEyMzEyMzU5NTlaMBIxEDAOBgNVBAMTB1p3akNlcnQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALfGfsiYpIVKu3gPJl790L13+CZWt6doePZHmcjMl+xPQKIR2fDvsCq9ZxzapDgiG4T3mgcVKUv55DBiuHcpXDvXt28m49AjdKwp924bOGKPM56eweKDCzYfLxy5SxaZfA9qjUhnPq3kGu1lfWjXbsp1rKI1UhKJg5b2j0V7AOC3AgMBAAGjRzBFMEMGA1UdAQQ8MDqAEH/MEXV8FHNLtxvllQ5SMbihFDASMRAwDgYDVQQDEwdad2pDZXJ0ghAIAOzFvLxLuUhHJRwHUUh9MAkGBSsOAwIdBQADgYEAdBtBNTK/Aj3woH2ts6FIU3nh7FB2tKQ9L3k6QVL+kCR9mHuqWtYFJTBKxzESN2t0If6muiktcO+C8iNwYpJpPzLAOMFMrTQhkO82gcdr9brQzMWPTraK1IS+GGH8QBIOTLx9zfV/iCIXxRub+Sq9dmRSQjKDeLeHWoE5I6FkQJg=" />
            
</
identity
>
        
</
endpoint
>
    
</
client
>
  
<
behaviors
>
    
<
endpointBehaviors
>
      
<
behavior 
name="Service1Nehavior">
        
<
clientCredentials
>
          
<
clientCertificate 
findValue="ZwjCert" x509FindType="FindBySubjectName" storeLocation="LocalMachine" storeName="TrustedPeople" />
        
</
clientCredentials
>
      
</
behavior
>
    
</
endpointBehaviors
>
  
</
behaviors
>
</
system.serviceModel
>

可以看出endpoint节点下的identity.certificate的encodedValue包含了加密的数据,另外需要手动增加clientCertificate配置信息,该信息表示证书在本地电脑存放的位置,当然也可以通过代码来动态指定,如:proxy.ClientCredentials.ClientCertificate.SetCertificate("ZwjCert", StoreLocation.LocalMachine, StoreName.My);

客户端使用服务代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
static 
void 
Main(
string
[] args)
{
    
using 
(
var 
proxy = 
new 
ServiceReference1.Service1Client())
    
{
 
        
//proxy.ClientCredentials.ClientCertificate.SetCertificate("ZwjCert", StoreLocation.LocalMachine, StoreName.My); //直接动态指定证书存储位置
        
string 
result = proxy.GetData(1);
        
Console.WriteLine(result);
        
var 
compositeObj = proxy.GetDataUsingDataContract(
new 
CompositeType() { BoolValue = 
true
, StringValue = 
"test" 
});
        
Console.WriteLine(SerializerToJson(compositeObj));
    
}
    
Console.ReadKey();
}

网上还有另类的针对X509证书验证,主要是采用了自定义的证书验证器类,有兴趣的可以参见这篇文章:

第三种:ASP.NET成员资格(membership)验证

 由于该验证需要借助于X509证书,所以仍然需要创建一个证书(方法如第一种中创建证书方法相同):ZwjCert;

由于该种验证方法是基于ASP.NET的membership,所以需要创建相应的数据库及创建账号,创建数据库,请通过运行aspnet_regsql.exe向导来创建数据库及其相关的表,通过打开ASP.NET 网站管理工具(是一个自带的管理网站),并在上面创建角色及用户,用于后续的验证;

这里特别说明一下,若采用VS2013,VS上是没有自带的GUI按钮来启动该管理工具网站,需要通过如下命令来动态编译该网站:

1
2
cd 
C:\Program Files\IIS Express
iisexpress.exe 
/path
:C:\Windows\Microsoft.NET\Framework\v4.0.30319\ASP.NETWebAdminFiles 
/vpath
:
/WebAdmin 
/port
:12345 
/clr
:4.0 
/ntlm

编译时若出现报错:“System.Configuration.StringUtil”不可访问,因为它受保护级别限制,请将WebAdminPage.cs中代码作如下修改:

1
2
3
4
5
6
7
8
//取消部份:
string 
appId = StringUtil.GetNonRandomizedHashCode(String.Concat(appPath, appPhysPath)).ToString(
"x"
, CultureInfo.InvariantCulture);
 
 
//新增加部份:
Assembly sysConfig = Assembly.LoadFile(
@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Configuration.dll"
);
Type sysConfigType = sysConfig.GetType(
"System.Configuration.StringUtil"
);
string 
appId = ((
int
)sysConfigType.GetMethod(
"GetNonRandomizedHashCode"
).Invoke(
null
new 
object
[] { String.Concat(appPath, appPhysPath), 
true 
})).ToString(
"x"
, CultureInfo.InvariantCulture);

这样就可以按照命令生生成的网址进行访问就可以了。如果像我一样,操作系统为:WINDOWS 10,那么不好意思,生成的网站虽然能够打开,但仍会报错:

遇到错误。请返回上一页并重试。

目前没有找到解决方案,网上有说ASP.NET网站管理工具在WIN10下不被支持,到底为何暂时无解,若大家有知道的还请分享一下(CSDN有别人的求问贴:),非常感谢,我这里就只好换台电脑来运行ASP.NET管理工具网站了。

 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
33
34
35
36
37
38
39
40
41
42
43
44
45
<
connectionStrings
>
  
<
add 
name="SqlConn" connectionString="Server=.;Database=aspnetdb;Uid=sa;Pwd=www.zuowenjun.cn;"/>
</
connectionStrings
>
<
system.web
>
  
<
compilation 
debug="true" targetFramework="4.5" />
  
<
httpRuntime 
targetFramework="4.5"/>
  
<
membership 
defaultProvider="SqlMembershipProvider">
    
<
providers
>
      
<
clear
/>
      
<
add 
name="SqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="SqlConn" applicationName="/" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" passwordFormat="Hashed"/>
    
</
providers
>
  
</
membership
>
</
system.web
>
<
system.serviceModel
>
  
<
behaviors
>
    
<
serviceBehaviors
>
      
<
behavior 
name="Service1Behavior">
        
<
serviceCredentials
>
          
<
serviceCertificate 
findValue="ZwjCert" storeLocation="LocalMachine"
            
storeName="TrustedPeople" x509FindType="FindBySubjectName" />
          
<
userNameAuthentication 
userNamePasswordValidationMode="MembershipProvider"
            
membershipProviderName="SqlMembershipProvider" />
        
</
serviceCredentials
>
         
<
serviceMetadata 
httpGetEnabled="true" httpsGetEnabled="true" />
        
<
serviceDebug 
includeExceptionDetailInFaults="false" />
      
</
behavior
>
    
</
serviceBehaviors
>
  
</
behaviors
>
  
<
bindings
>
    
<
wsHttpBinding
>
      
<
binding 
name="Service1Binding">
        
<
security 
mode="Message">
          
<
message 
clientCredentialType="UserName"/>
        
</
security
>
      
</
binding
>
    
</
wsHttpBinding
>
  
</
bindings
>
  
<
serviceHostingEnvironment 
aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<
services
>
  
<
service 
name="WcfService1.Service1" behaviorConfiguration="Service1Behavior">
    
<
endpoint 
address="" binding="wsHttpBinding" contract="WcfService1.IService1" bindingConfiguration="Service1Binding">
    
</
endpoint
>
  
</
service
>
</
services
>
</
system.serviceModel
>

这里需注意几点:

1.配置connectionString,连接到membership所需的数据库; 

2.配置membership,增加SqlMembershipProvider属性配置;

3.配置serviceCredential,与第一种基本相同,不同的是userNameAuthentication的配置:userNamePasswordValidationMode="MembershipProvider",membershipProviderName="SqlMembershipProvider";

4.配置Binding节点<message clientCredentialType="UserName"/>,这与第一种相同; 

客户端引用WCF服务,查看生成的配置文件内容,需确保Binding节点有以下配置信息:

1
2
3
<
security 
mode="Message">
        
<
message 
clientCredentialType="UserName" />
 
</
security
>

最后使用WCF服务,使用代码与第一种相同,唯一需要注意的是,传入的UserName和Password均为ASP.NET网站管理工具中创建的用户信息。

另外我们也可以采用membership+Form验证,利用ASP.NET的身份验证机制,要实现这种模式,是需要采用svc文件,并寄宿在IIS上,具体实现方法,参见:

转载于:https://www.cnblogs.com/saodiseng2015/p/9270506.html

你可能感兴趣的文章
Fiddler抓包后保存为JMX(jmeter脚本,不限jmeter使用版本)
查看>>
[SimplePlayer] 3. 视频帧同步
查看>>
UVA 11027 - Palindromic Permutation
查看>>
Android LayoutInflater原理分析
查看>>
AS不能真机调试 (转)
查看>>
SQL SERVER代码生成器必备
查看>>
使用NET USE将USB端口模拟为LPT1
查看>>
二维数组和指向指针的指针
查看>>
BBS项目(四)
查看>>
IaaS,PaaS和SaaS
查看>>
中山纪念中学 培训 日记
查看>>
LeetCode "Best Meeting Point" !
查看>>
《实时控制软件设计》第四周作业
查看>>
Valid Word Abbreviation Leetcode
查看>>
SQL数据库 开启时出现 数据库连接错误2,error:40的问题。如何解决
查看>>
新阶段
查看>>
屏幕适配
查看>>
mysql使用索引优化查询效率
查看>>
lca转RMQ
查看>>
HTML5+CSS3 效果网站集合
查看>>