【WCF】使用“用户名/密码”验证的合理方法
我不敢说俺的方法是最佳方案,反正这世界上很多东西都是变动的,正像老子所说的——“反(返)者,道之动”。以往看到有些文章中说,为每个客户端安装证书嫌麻烦,就直接采用把用户名和密码塞在SOAP头中发送,然后在服务器端自定义一个消息拦截器来验证用户名和密码。
以老周不靠谱的学识水平认为,这样做不好,直接把明文而且敏感信息放在SOAP头中传输,这尺度实在太大了,太暴露了,广电局是不会允许的,虽然现在流行穿得越少越好,但那些是婊子的境界。像用户名密码这些重要信息,怎么能直接传输呢,这很容易被偷窥的。
其实,WCF并不要求客户端必须安装服务器证书。证书就好比身份证,用来标识你的身份的,所以你不能拿着你外公的身份证去实名买火车票,除非售票的人是傻逼才会卖给你。
一般来说,传输层的安全是不需要提供证书的,但是消息安全是要求服务器提供证书的,客户端可以以证书为凭据,也可以不提供,即只对服务器有要求,客户端可选择其他凭据,如Windows验证。经过老周试验,如果客户端凭据为Windows,那么服务器也可以不设置证书的。不过,对用户名/密码验证方式,服务器必须提供证书。
说白了,根本不必为每个客户端安装服务器证书,完全不应该把用户名密码放在Header里面,那样太危险。
说再多废话意义不大,还是来个例子有用。
要实现自定义的用户名密码验证,需要实现一个抽象类——UserNamePasswordValidator。
class CustUsernamepwdValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (userName != "admin" || password != "")
{
throw new Exception("用户名或密码错误。");
}
}
}
实现方法是重写Validate方法,两个参数分别表示用户名和密码,注意,这个方法返回void,所以在检查用户名和密码时,如果用户名或密码不正确,你直接抛出异常就行了。无异常发生,表示验证成功;抛出异常,表示验证失败。
客户端使用用户名密码凭据时,服务器必须提供证书,用以对消息进行签名和加密。
host.Credentials.ServiceCertificate.SetCertificate("CN=localhost", StoreLocation.LocalMachine, StoreName.My);
用户名密码验证有这么几个方案:
a、Windows 帐号,此法适合内网。
b、ASP.NET集成的成员资格认证,这个应该很熟悉吧,就是那个拖控件就能完成的东东。在ASP.NET 2.0时引入。
c、自定义方法。我们这个例子就是用这种方案,自定义方案要实现UserNamePasswordValidator抽象类。
下面代码设置自定义用户名密码验证方案,让验证逻辑和我们刚才定义的验证类关联。
host.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;
host.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustUsernamepwdValidator();
这很简单,两步,第一步指明实现用户名密码验证方式为自定义,第二步就是关联自定义验证器。
我这里是用代码配置的,也可以用配置文件来配置,原理一样,我就不弄了,代码设置和配置文件设备任选一种即可。
在调用服务时,由于客户端不安装服务器证书,所以在调用之前,应该将客户端对服务器证书的信任方式改为None,这样就不会发生异常了。
ChannelFactory<IDemo> fac = new ChannelFactory<IDemo>(binding, epaddr);
// 以下设置可以忽略客户端对服务器证书的信任验证
fac.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;
然后客户端要设置用户名和密码。
// 设置用户名和密码
fac.Credentials.UserName.UserName = usName;
fac.Credentials.UserName.Password = passWord;
我们这个例子的验证很简单,只为了演示,如果用户名为admin,密码为321就是正确。
在创建EndpointAddress时,为了保证客户端访问到正确的服务,可以设置一个DNS的标识。
EndpointAddress epaddr = new EndpointAddress(new Uri("http://...."), EndpointIdentity.CreateDnsIdentity("localhost"));
如果使用DNS为标识,那么服务器证书的主题名字必须是服务器的主机名,比如这里的localhost是本机主机名,即服务器使用的证书名必须为localhost。其实当你装好系统后,会自动生成以主机名命名的证书的,你不必自己创建证书。
要是服务器的证书的主题名字不是主机名,那么客户端在调用服务时,附加给终结点地址的标识可以使用服务器证书的公钥。总之,终结点标识的作用是:让客户端能访问到正确的服务器,而不是山寨假冒的服务主机。比如我本来是要调用F服务器上的服务的,结果由于办公室里有汉奸,把情报出卖给恶意攻击者,攻击者就伪装成F服务让我去调用,有了终结点标识,就可以避免误调。
例子运行效果如下。

你看,这样做就比把用户名密码放在Header里面安全多了,而且客户端是不用安装服务器证书的。
要想知道消息是否被加密,一种方法不用第三方工具,老周以前写过,可以配置记录SOAP消息日志,这样就可以看到通信的SOAP消息了。
要是想用抓包工具的话,可以考虑用TcpTrace,这个小玩意儿,准确说应该叫端口转发。
我们刚刚的例子,服务器是在7000端口上侦听的,现在为了让TcpTrace能够拦截消息,我们可以让客户端中所访问的终结点地址的端口号改为 7001,然后让TcpTrace在7001处拦截,再转发到7000端口。

这样一来,既能拦截消息,又不会影响服务器和客户端的正常通信,只不过是中途进行了转发罢了,实际上消息最终还是会发送到7000端口的。
但是,运行后你会发现,消息是拦截到了,但是转发到7000端口后,服务器出错了,那是因为在消息安全模式下,服务器会验证To标头中的地址是否与服务器的侦听地址匹配,由于要让TcpTrace在中途截杀,所以客户端其实把To地址改为了7001,这与服务器的实际端口7000值不匹配,出于安全考虑,在安全模式是不能调用服务的。
要解决这个也好办,只要在实现服务协定的类上加上这个即可。
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
internal sealed class DemoService : IDemo
{
public void SaySomething(string msg)
{
……
}
}
Any表示任意地址均可匹配成功,注意,是加到实现服务协定的类上,不要加到协定接口上。
然后用TcpTrace拦截一下,看到没,消息都加密了。

好了,今天的F话就说到这里了。
【WCF】使用“用户名/密码”验证的合理方法的更多相关文章
- 【WCF】Silverlight+wcf+自定义用户名密码验证
本文摘自 http://www.cnblogs.com/virusswb/archive/2010/01/26/1656543.html 在昨天的博文Silverlight3+wcf+在不使用证书的情 ...
- 自定义实现wcf的用户名密码验证
目前wcf分为[传输层安全][消息层安全]两种,本身也自带的用户名密码验证的功能,但是ms为了防止用户名密码明文在网络上传输,所以,强制要求一旦使用[用户名密码]校验功能,则必须使用证书,按照常理讲, ...
- WCF用户名密码验证方式
WCF使用用户名密码验证 服务契约 namespace WCFUserNameConstract { [ServiceContract] public interface IWcfContract { ...
- WCF服务安全控制之netTcpBinding的用户名密码验证【转】
选择netTcpBinding WCF的绑定方式比较多,常用的大体有四种: wsHttpBinding basicHttpBinding netTcpBinding wsDualHttpBinding ...
- WCF 安全性之 自定义用户名密码验证
案例下载 http://download.csdn.net/detail/woxpp/4113172 客户端调用代码 通过代理类 代理生成 参见 http://www.cnblogs.com/woxp ...
- WebService 用户名密码验证
原文:WebService 用户名密码验证 在项目开发的过程中,WebService是经常要用的,当调用WebService方法时,需要经过服务的验证才可以调用,一般就是用户名/密码验证,还有一个就是 ...
- WCF的用户名+密码认证方式(转)
概述 今天在做Master Data Service(后面简称MDS)项目时需要通过WCF来使用MDS的API,从而对MDS的数据进行操作.在这个过程中,遇到了一个棘手的问题,就是在客户端调用Web ...
- OpenVPN使用用户名/密码验证方式
OpenVPN推荐使用证书进行认证,安全性很高,但是配置起来很麻烦.还好它也能像pptp等vpn一样使用用户名/密码进行认证. 不管何种认证方式,服务端的ca.crt, server.crt, ser ...
- mysql用户名密码忘记了解决方法
今天想用一下实验室服务器的mysql,发现不记得用户名密码了. 解决方法如下: 1. 保证服务器处于安全的状态,如果可以请拔掉网线...(不过我跳过了这一步,额) 2. 修改/etc/my.cnf文件 ...
随机推荐
- 避免重复造轮子的UI自动化测试框架开发
一懒起来就好久没更新文章了,其实懒也还是因为忙,今年上半年的加班赶上了去年一年的加班,加班不息啊,好了吐槽完就写写一直打算继续的自动化开发 目前各种UI测试框架层出不穷,但是万变不离其宗,驱动PC浏览 ...
- javascript中的this与函数讲解
前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域.并且,大家可以认为全局作用域其实就是Window函数的函数作用域,我们编写的js代码, ...
- 匹夫细说C#:庖丁解牛迭代器,那些藏在幕后的秘密
0x00 前言 在匹夫的上一篇文章<匹夫细说C#:不是“栈类型”的值类型,从生命周期聊存储位置>的最后,匹夫以总结和后记的方式涉及到一部分迭代器的知识.但是觉得还是不够过瘾,很多需要说清楚 ...
- 随手记_C#验证码
前言 最近在网上偶然看见一个验证码,觉得很有意思,于是搜了下,是使用第三方实现的,先看效果: 总体来说效果还是可以的,官方提供的SDK也比较详细,可配置性很高.在这里在简单啰嗦几句使用方式: 使用步骤 ...
- 初步了解nodejs
什么是Node.js? 很多初学者并没有真正地理解Node.js到底是什么.nodejs.org网站中的描述也没有多大帮助. 首先要清楚Node不是一个Web服务器,这十分重要.它本身并不能做任何事情 ...
- dubbo服务提供与消费
一.前言 项目中用到了Dubbo,临时抱大腿,学习了dubbo的简单实用方法.现在就来总结一下dubbo如何提供服务,如何消费服务,并做了一个简单的demo作为参考. 二.Dubbo是什么 Dubbo ...
- Carousel 旋转画廊特效的疑难杂症
疑难杂症 该画廊特效的特点就是前后元素有层级关系. 我想很多人应该看过或者用过这个插件carousel.js,网上也有相关的教程.不知道这个插件的原型是哪个,有知道的朋友可以告诉我. 该插件相对完美, ...
- C++ 11 多线程--线程管理
说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...
- ASP.NET Core 中文文档 第四章 MVC(4.1)Controllers, Actions 和 Action Results
原文:Controllers, Actions, and Action Results 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:许登洋(Seay) Action 和 acti ...
- 微信小程序IDE(微信web开发者工具)安装、破解手册
1.IDE下载 微信web开发者工具,本人是用的windows 10 x64系统,用到以下两个版本的IDE安装工具与一个破解工具包: wechat_web_devtools_0.7.0_x64.exe ...