WCF权限认证多种方式
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证书验证,主要是采用了自定义的证书验证器类,有兴趣的可以参见这篇文章:http://www.cnblogs.com/ejiyuan/archive/2010/05/31/1748363.html
第三种:ASP.NET成员资格(membership)验证
由于该验证需要借助于X509证书,所以仍然需要创建一个证书(方法如第一种中创建证书方法相同):ZwjCert;
由于该种验证方法是基于ASP.NET的membership,所以需要创建相应的数据库及创建账号,创建数据库,请通过运行aspnet_regsql.exe向导来创建数据库及其相关的表,通过打开ASP.NET 网站管理工具(是一个自带的管理网站),并在上面创建角色及用户,用于后续的验证;
这里特别说明一下,若采用VS2013,VS上是没有自带的GUI按钮来启动该管理工具网站,需要通过如下命令来动态编译该网站:
|
1
2
|
cd C:\Program Files\IIS Expressiisexpress.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有别人的求问贴:http://bbs.csdn.net/topics/391819719),非常感谢,我这里就只好换台电脑来运行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上,具体实现方法,参见:http://www.cnblogs.com/danielWise/archive/2011/01/30/1947912.html
WCF权限认证多种方式的更多相关文章
- SpringBoot之SpringSecurity权限注解在方法上进行权限认证多种方式
前言 Spring Security支持方法级别的权限控制.在此机制上,我们可以在任意层的任意方法上加入权限注解,加入注解的方法将自动被Spring Security保护起来,仅仅允许特定的用户访问, ...
- Cognos权限认证CJP方式之用户密码加密
在项目开发过程中,用户往往对系统的安全都有明确的要求,下面针对cognos门户认证用户密码如何加密来提供一个简单的wf 1Cognos权限认证方式:CJP 2Cognos用户数据库类型:Oracle ...
- ansible-playbook权限提升多种方式
ansible-playbook 可以方便快速的批量执行部署和运维任务,对于不同的场景和服务器,需要使用不同的权限提升方式. 最佳实现:为了提高playbook的兼容性,跟功能没有直接关系的权限提升脚 ...
- Restful风格wcf调用4——权限认证
写在前面 在前面的三篇文章,已经介绍了restful风格wcf,如何实现增删改查以及文件的上传下载操作.本篇文章将介绍一下,调用restful的权限认证的内容.在调用的接口,为了安全,总会需要对请求进 ...
- 比RBAC更好的权限认证方式(Auth类认证)
Auth 类已经在ThinkPHP代码仓库中存在很久了,但是因为一直没有出过它的教程, 很少人知道它, 它其实比RBAC更方便 . RBAC是按节点进行认证的,如果要控制比节点更细的权限就有点困难了, ...
- thinkphp Auth认证类 比RBAC更好的权限认证方式(Auth类认证)
thinkphp Auth认证类 比RBAC更好的权限认证方式(Auth类认证) Auth 类已经在ThinkPHP代码仓库中存在很久了,但是因为一直没有出过它的教程, 很少人知道它, 它其实比 ...
- thinkphp 比RBAC更好的权限认证方式(Auth类认证)
Auth 类已经在ThinkPHP代码仓库中存在很久了,但是因为一直没有出过它的教程, 很少人知道它, 它其实比RBAC更方便 . RBAC是按节点进行认证的,如果要控制比节点更细的权限就有点困难了, ...
- WCF权限控制
前面写了 WCF账户密码认证, 实现了帐号密码认证, 接下来看看如何对方法的细粒度控制, 本文很大程度参考了 WCF安全之基于自定义声明授权策略, 这篇文章对原理讲得比较清楚, 而我这篇文章呢, ...
- (WCF初体验)WCF的认证和消息保护
最近做WCF开发,有个需求是在服务端做认证,网上查资料了解到可以用UserName和Password 来做认证,只需要写好配置文件和在服务端写好验证类就行了,但是网上普遍的博文都是需要用证书,而我自己 ...
随机推荐
- HDFS网络拓扑概念及机架感知(副本节点选择)
网络拓扑概念 在本地网络中,两个节点被称为“彼此近邻”是什么意思?在海量数据处理中,其主要限制因素是节点之间数据的传输速率——带宽很稀缺.这里将两个节点间的带宽作为距离的衡量标准. 节点距离:两个节点 ...
- AtCoder Beginner Contest 133 B - Good Distance
地址:https://atcoder.jp/contests/abc133/tasks/abc133_b 核心问题:判断一个浮点数开方是否为整数 ; double ans1=sqrt(ans); if ...
- Python的魔法方法??
就是可以给你的类增加魔力的特殊方法,如果你的对象实现 (重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用,你可以定义自己想要的行为,而这一切都是自动发生的. __in ...
- [AtCoder ARC076] F Exhausted?
霍尔定理 + 线段树? 咱学学霍尔定理... 霍尔定理和二分图完美匹配有关,具体而言,就是定义了二分图存在完美匹配的充要条件: 不妨设当前二分图左端集合为 X ,右端集合为 Y ,X 与 Y 之间的边 ...
- Ubuntu 安装 ansible
sudo apt update sudo apt-get install software-properties-common sudo apt-add-repository --yes ppa:an ...
- 6个优秀的微信小程序ui组件库
开发微信小程序的过程中,选择一款好用的组件库,可以达到事半功倍的效果.自从微信小程序面世以来,不断有一些开源组件库出来,下面6款就是排名比较靠前,用户使用量与关注度比较高的小程序UI组件库.还没用到它 ...
- 修改 linux 默认字符集
[root@eric6 ~]# cat /etc/sysconfig/i18n //查看 linux 默认的字符集,默认是 UTF-8 LANG="zh_CN.UTF-8" cp ...
- 一、Vs2019扩展多了 导航到反编译的源码中运行
一.导航到反编译的源码中运行
- 零点.Net Core 接触
一.Program.cs类与Startup类 1.一切从Main开始,Main方法包含了是整个应用程序的入口 ASP.NET Core应用程序可以配置和启动主机(Host). 主机负责应用程序启动和生 ...
- Java 静态方法、私有方法、常量的使用
1.静态方法 2.私有方法 3.常量 4.接口小结