发文章
发文工具
撰写
网文摘手
文档
视频
思维导图
随笔
相册
原创同步助手
其他工具
图片转文字
文件清理
AI助手
留言交流
第九篇:WCF安全 - 自定义认证
接着上一篇,我们尝试一下用自定义用户名密码的方式来做安全认证,这样就不用受制于Windows的用户系统了。
首先需要说明的是,使用自定义用户名密码时,由于不能利用Windows用户系统的相关安全机制了,因此必须自己准备数字证书来处理数据加密。
1、准备数字证书
证书要求有可进行密钥交换的私钥,一般用makcert比较方便,自带的,也可以用OpenSSL或OpenSSL.Net一类的。本例中生成一个自签名的根证书,放入系统的受信根证书颁发机构区:
makecert -n "CN=192.168.90.81" -b 01/01/2012 -e 01/01/2050 -r -sky exchange -sr LocalMachine -ss Root -a sha1 参数含义: -n 指定使用者为192.168.90.81,注意它必须和调用时指定的域名一致 -b 起始日期为2012-1-1 -e 失效日期为2050-1-1 -r 自签名 -sky exchange 指定是密钥交换型而不是签名型 -sr LocalMachine 存入本地计算机 -ss Root 存入受信任根证书颁发机构区 -a sha1 使用SHA1签名
2、服务端
服务端要修改几处,首先是配置文件App.config:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services> <!--在上一篇的基础上加了behaviorConfiguration,内容见后--> <service name="Server.DataProvider" behaviorConfiguration="tcpBehavior"> <endpoint address="" binding="netTcpBinding" contract="Server.IData" bindingConfiguration="tcpBinding" /> <host> <baseAddresses> <add baseAddress="net.tcp://localhost:8081/wcf" /> </baseAddresses> </host> </service> </services> <bindings> <netTcpBinding> <binding name="tcpBinding"> <security mode="Message"> <!--与上一篇相比,认证类型从Windows改成了UserName--> <message clientCredentialType="UserName" /> </security> </binding> </netTcpBinding> </bindings> <!--这是新加的节,用于指定用户名密码的验证方式--> <behaviors> <serviceBehaviors> <!--注意这个name是被前面使用的--> <behavior name="tcpBehavior"> <serviceCredentials> <!--指定验证方式为Custom,表示自定义,既然是自定义的,就要指出用哪个类进行用户名密码验证,这里指定了Server程序集中的Server.Validator类,注意这里类完整名称的写法--> <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Server.Validator, Server" /> <!--指定用于数据加密的证书,LocalMachine表示本地计算机,Root表示受信任根证书颁发机构,192.168.90.81是证书标题(因为做证书时没指定标题,所以使用者默认就是标题),FindBySubjectName表示按标题查找--> <serviceCertificate storeLocation="LocalMachine" storeName="Root" findValue="192.168.90.81" x509FindType="FindBySubjectName" /> </serviceCredentials> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
接下来是自定义的验证类,增加一个Validator类,它要继承System.IdentityModel.Selector.UserNamePasswordValidator基类。
using System; using System.IdentityModel.Selectors; using System.ServiceModel; namespace Server { public class Validator : UserNamePasswordValidator { //重写Validate方法,这里简化处理,直接写死用户名密码,实际应用中应结合DB、配置文件等来做验证 public override void Validate(string userName, string password) { if(!string.Equals(userName, "root") || !string.Equals(password, "pass")) throw new Exception("Access Denied"); } } }
最后我们还要小改一下契约接口的实现类,因为之前我们用WindowIdentity来识别登录用户,换用自定义用户名密码后,它不管用了,所以新的实现类是这样的:
using System; using System.ServiceModel; namespace Server { [ServiceBehavior] public class DataProvider : IData { public string SayHello() { //变化不大,用PrimaryIdentity来代替WindowIdentity return string.Format("Hello {0}", OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name); } } }
OK,运行一下,应该能正常启动,如果失败,仔细看一下提示信息,一般都是证书问题。
3、客户端
变化不大,先来看配置文件App.config:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <client> <endpoint binding="netTcpBinding" contract="Server.IData" address="net.tcp://192.168.90.81:8081/wcf" name="DataProvider" bindingConfiguration="tcp" /> </client> <bindings> <netTcpBinding> <binding name="tcp"> <security mode="Message"> <!--只有此处把Windows改成了UserName,和服务端对应--> <message clientCredentialType="UserName" /> </security> </binding> </netTcpBinding> </bindings> </system.serviceModel> </configuration>
然后是调用时用户名密码的传递方式变了一点:
using System; using System.ServiceModel; using System.ServiceModel.Channels; namespace Client { class Program { static void Main(string[] args) { //创建一个ChannelFactory,指定使用名为DataProvider的配置 var factory = new ChannelFactory<Server.IData>("DataProvider"); //指定用户名、密码,和前一篇的区别是把Windows换成了UserName factory.Credentials.UserName.UserName = "root"; factory.Credentials.UserName.Password = "pass"; //创建Channel,并调用SayHello方法 var proxy = factory.CreateChannel(); Console.WriteLine(proxy.SayHello()); ((IChannel)proxy).Close(); } } }
一切就绪,运行一下吧,应该能看到“Hello root”。如果用户名密码错误,会收到Exception。
如果服务端启动失败,请检查:
◇ 证书是否有可交换的密钥 ◇ 证书是否正确导入了系统 ◇ 按服务端App.config中指定的证书查找方式是否可找到证书 ◇ 指定的自定义验证类名称是否错误
如果客户端访问失败,请检查:
◇ 是否提供了正确的用户名密码 ◇ 服务端证书有效期是否合法 ◇ 服务端证书的证书链是否完整 ◇ 客户端访问时使用的域名/IP是否与服务端证书的使用者一致
OK,安全问题就讲到这里吧,既然是简单教程,就不继续深入了。
================================================================
如果你有一个真正的X509证书,那么现在的代码就可以正常运行了。但是很不幸,我们的证书是测试用的,我们运行的时候出错:'X.509 certificate CN=MyServerCert 链生成失败。所使用的证书具有无法验证的信任链。请替换该证书或更改 certificateValidationMode。已处理证书链,但是在不受信任提供程序信任的根证书中终止',WCF无法验证测试证书的信任链,那我们要做的就是绕过这个信任验证,具体做法如下:
先要在Asp.net Web应用程序项目上添加引用'System.IdentityModel'然后我们建立一个新的类文件并继承自'System.IdentityModel.Selectors.X509CertificateValidator',然后我们重写里面的'Validate'方法来实现我们自己的X509认证逻辑,代码如下:
using System;
using System.Configuration;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Cryptography.X509Certificates;
namespace ClientWeb.CustomX509Validator
{
/// <summary>
/// Implements the validator for X509 certificates.
/// </summary>
public class MyX509Validator: X509CertificateValidator
/// Validates a certificate.
/// <param name="certificate">The certificate the validate.</param>
public override void Validate(X509Certificate2 certificate)
// validate argument
if (certificate == null)
throw new ArgumentNullException("X509认证证书为空!");
// check if the name of the certifcate matches
if (certificate.SubjectName.Name != ConfigurationManager.AppSettings["CertName"])
throw new SecurityTokenValidationException("Certificated was not issued by thrusted issuer");
}
你可以把Validate方法里面留空让所有的认证都通过,也可以自己定义认证逻辑,如果认证失败,就抛出'SecurityTokenValidationException'的异常,然后我们配置一下客户端的webconfig让它使用我们自己的X509认证,增加以下的配置节,并在'endpoint'节中指定behaviorConfiguration="myClientBehavior"。
<behaviors>
<endpointBehaviors>
<behavior name="myClientBehavior">
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="Custom" customCertificateValidatorType="ClientWeb.CustomX509Validator.MyX509Validator,ClientWeb" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
OK,客户端代码和配置完成,现在你可以运行自己的程序了,运行界面如下:如果需要源代码在这里下载.
来自: 昵称10504424 > 《工作》
0条评论
发表
请遵守用户 评论公约
一步一步学Linq to sql(十):分层构架的例子
一步一步学Linq to sql(十):分层构架的例子。l C引用B、D、System.ServiceModel以及System.Data.Linq程序集。首先我们可以定义出留言簿数据访问服务的契约(接口),把如下的代码保存为IDat...
WCF身份验证之用户名密码认证
WCF身份验证之用户名密码认证。WCF支持多种认证技术,例如Windowns认证、X509证书、Issued Tokens、用户名密码认证等,在跨Windows域分布的系统中,用户名密码认证是比较常用的,要实现用户名密码认证...
抛弃WebService 在.NET4中用jQuery调用WCF
抛弃WebService 在.NET4中用jQuery调用WCF.在.NET 3.5时代,jQuery就可以调用WCF,之前我也写过博客(比如:jQuery调用WCF需要注意的一...
WCF 开启消息日志功能
WCF 开启消息日志功能生产环境。若要增强调试功能,也应将其他跟踪源(System.ServiceModel.MessageLogging)添加到配置中以启用消息日志记录。--开启日志功能--> <system.diagnostics> <t...
Could not load file or assembly ''System.Private.ServiceModel, Version=4.1.2.4, Culture=neut
ServiceModel, Version=4.1.2.4, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a''<PackageReference Include="System.ServiceModel.Duplex" Version="4.5.0" />...
WCF之旅(1):创建一个简单的WCF程序
这个Project引用Artech.WCFService.Contract、Artech.WCFService.Service和System.ServiceModel DLL。//Create a Binding for Endpoint....
WCF配置文件全攻略
—定义一个或多个系统提供的binding元素,例如<basicHttpBinding> --> <!—也可以是自定义的binding元素,如<customBinding>. --> <binding> <!—例如<...
WCF创建客户端服务对象方法解析 1
WCF创建客户端服务对象方法解析 1.除了上面的生成代理类的方法,如果我们知道了元数据的地址,还可以通过自己的代码实现代理类的生成,...
WCF编程系列(一)初识WCF
下面通过一个简单的服务示例来认识WCF(只需让本例顺利运行即可,关于代码中的各种类型及WCF的相关概念我们将在后续介绍):1.新建项目,名称 XfrogWCFService,解决方案名称 XfrogWCFStudy001,模板选择...
微信扫码,在手机上查看选中内容