用WSE在Web服务中验证用户身份
毫无疑问,SOAP和XML Web服务在交互操作和标准上已经完全改变了电子商务领域的格局。
然而直到最近,在Web服务技术领域仍然存在着一些缺陷,那就是处理消息级别的安全、认证、加密、数字签名、路由和附件等问题的能力。为了解决这些安全问题,像IBM、Microsoft和Verisign这样的公司和组织正牵头合作制定统一的Web服务安全规范,以便利用它们原有的Web服务交互操作概念和商业模型,他们推出了WS-Security等规范。可以这么说,自从SOAP规范形成以后,WS-Security规范及其后续的工作可能是Web服务技术领域的一次最重要的进步。
随着WS-Security规范的定稿,各大软件厂商开始认真地考虑为其产品提供使用相同Web服务安全语言的接口和编程工具箱,Web服务开发者也将能够使用这些厂商提供的工具加强他们所开发的Web服务的安全性。
二、WSE安全性能简介
Microsoft推出了Web Services Enhancements 1.0 for .NET(以下简称WSE),它是一个类库,用于实现高级 Web 服务协议,这也是该公司的第一个使用WS-Security等规范实现SOAP消息安全的工具套件。
保护Web服务安全的一个很重要的环节就是保护其SOAP消息传递的安全。
使用WSE后,SOAP消息可以自己验证其完整性,并可使用定义在WS-Security规范中的机制加密。
WSE1.0支持的所有WS-Security特性都是通过实现SecurityInputFilter和 SecurityOutputFilter对象的安全性输入输出过滤器实现的,它支持的安全特性有:
1. 数字签名
2. 加密
3. 使用用户名令牌签名并加密
4. 使用X.509证书签名并加密
5. 使用自定义二进制令牌签名并加密
WSE1.0不支持Security Assertion Markup Language(SAML,安全声明标注语言),但Microsoft公司正积极在其.NETServer中实现SAML体系结构。当然,开发者自己可以自由的实现SAML。唯一的不足是还不能使用WSDL描述遵循WS-Security规范的Web服务的WS-Security接口。
WSE的体系结构模型基于处理入站和出站SOAP消息的过滤器管道。它是建立在已有的SOAPExtension类的基础上的,有使用过SOAPExtension类行进压缩、加密、记录和其它操作经验的开发者会发现他们对WSE其实很熟悉。
WSE提供了一个Microsoft.Web.Services.SoapContext类,让我们可以处理WS-Security SOAP头和其它入站的SOAP消息头,同时可为出站的SOAP消息添加WS-Security头。WSE还有一个包装类为SOAP请求和响应添加SOAPContext(与HttpContext类似),同时服务器使用一个SOAPExtension类“Microsoft.Web.Services.WebServicesExtension”,让我们可以验证入站的SOAP消息,还提供了我们可从我们的WebMethod中访问的请求和响应SoapContext。
学习使用WSE最大的障碍在于有时很难理解Microsoft的技术文档和相关文章,即使对于那些有丰富经验的高级开发人员来说也是如此,并且关于这方面的文章很少。在本文中,我将给出一个简单的例子,介绍如何使用WSE实现基本的用户名令牌的验证过程,以保证Web服务的安全。
三、设置WSE环境
为了设置基本的WSE环境,我们需要配置ASP.NET应用程序,使其能够使用WSE SOAPExtension。最简单的方法是把所需的/configuration/system.web/webServices/soapExtensionTypes/Add元素添加到你的Web服务虚拟目录中的web.config里,如下所示:
<webServices> <soapExtensionTypes> <add type="Microsoft.Web.Services.WebServicesExtension, Microsoft.Web.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" priority="1" group="0" /> </soapExtensionTypes> </webServices> |
注意type属性必须写在一行中,但是在文中考虑到篇幅的问题需要把它分为几行,所以请读者多加注意。而且要注意,在开始使用WSE之前,我们必须在工程中加入对Microsoft.Web.Services.dll的引用。
四、基本的用户名令牌认证
在我们数字签名SOAP消息之前,必须先弄清楚谁正在签名。因此,我们将探讨一下用户名令牌(UsernameToken)的概念,同时了解WSE如何允许我们验证用户名令牌。
为了在Web服务中使用WSE验证用户名/密码,我们需要知道WSE在这方面为我们提供了什么?WS-Security定义了一个UsernameToken元素,它提供了基本用户名/密码验证的方法。如果你有使用HTTP的经验,那么你会发现UsernameToken与Basic Authentication非常类似。有三种用户名令牌,但是通常情况下我们只对最后两种最感兴趣:
<!--明文密码--> <UsernameToken> <Username>user1</Username> <Password Type="wsse:PasswordText">suangywang</Password> </UsernameToken> |
这种方法使用明文密码。我们不难想象,在服务器上将进行核对数据库,验证用户名与密码,看是否有匹配的用户名/密码对这一系列验证操作。
<!--密码摘要--> <UsernameToken> <Username>user1</Username> <Password Type="wsse:PasswordDigest"> QSMAKo67+vzYnU9TcMSqOFXy14U= </Password> </UsernameToken> |
这种方法发送一个密码摘要(digest)代替明文密码。使用密码摘要,密码就不会通过网络发送,这样黑客就不太可能算出Web服务的密码。密码摘要是用散列函数计算的。这个过程只是单向的,意味着将函数反向并找到对应于摘要的消息是不可能的,因为该过程以这样一种方式实现,所以找到散列到同一摘要的两条不同密码在计算上难以实现。但是黑客可以发送散列密码,然后冒充原始发送人被验证。为了避免这个问题,Web Services Security Addendum(Web服务安全补遗)已经增加一个辅助的保护措施。补遗中规定必须发送密码的摘要版本,而不仅仅发送散列密码。这个摘要信息包含一个密码散列,标识请求的唯一的Nonce和创建时间。因此绝对不会出现相同的两个密码散列。如下所示是修正后的用户名令牌UsernameToken。
<!--修正后的用户名令牌--> <wsse:UsernameToken xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" wsu:Id="SecurityToken-59845323-5dcb-4a6b-a7fb-94a0d7357a20"> <wsse:Username>User1</wsse:Username> <wsse:Password Type="wsse:PasswordDigest"> gpBDXjx79eutcXdtlULIlcrSiRs= </wsse:Password> <wsse:Nonce> h52sI9pKV0BVRPUolQC7Cg== </wsse:Nonce> <wsu:Created>2003-6-20T21:16:50Z</wsu:Created> </wsse:UsernameToken> |
虽然每个合法请求都有一个不同的散列,但是你也必须防止恶意用户把其他用户的合法请求中的整个UsernameToken拿出放入自己的非法请求中。你可以使用Timestamp(时间戳标头)来最小化这种危险。时间戳标头用来表示消息的创建时间和过期时间,指明消息的周期以及何时可以认为该消息失效。 例如,你可能想指定消息在40秒以后失效,并且超过40秒服务器就不会接收UsernameToken。但是机器之间的时钟同步问题可能会造成有效的请求被拒绝的情况。所以使用时间戳也并不是一个尽善尽美的解决方法。为了解决这个问题,Web服务可以保存一张最近收到的UsernameToken的Nonce值的表,如果收到的一个请求的Nonce值已经被使用了,那么就绝对不会接受这个请求。如果你接收几个使用相同Nonce的请求,那么你要考虑把这几条请求全部丢弃,因为很有可能先到的请求是非法请求。还要了解到Nonce核对技术并不能防止恶意用户截获合法的输入信息,并把原始信息中的UsernameToken加入自己的消息,然后发送到目的地。这时就需要为消息添加数字签名或安全证书,以保护其不受攻击。数字签名和安全证书的相关知识在本文中不会涉及,请读者查阅相应文献。
所有的散列保护都需要消息发送端和接收端知道用户的密码。在客户端,人们期望系统能够提示用户输入密码。而在服务器端,需要保存带有有效用户名/密码对的表,以供系统查找。我们下面将介绍WSE如何使用一个Password Provider(密码提供者)机制来解决这两个问题。
五、IPasswordProvider接口
WSE定义了一个Microsoft.Web.Services.Security.IPasswordProvider接口类,我们必须实现这个类来注册一个密码提供者。这个接口有一个方法GetPassword,它接收一个Microsoft.Web.Services.Security.UsernameToken作为输入参数,该方法返回指定用户的密码。其思想是你可以使用任何你想用的机制保存有效的用户名/密码对,然后提供了一个实现IPasswordProvider接口的类,来让WSE访问你的特定密码存储机制。你甚至可以执行你自己的UsernameToken的摘要(Digest)和散列(Hash)的组合,甚至使用一个共享的密码,以进一步控制你的认证基础结构。
为了把你特定的Password Provider(密码提供者)告诉WSE,你必须配置合适的WSE设置。首先要添加一个Microsoft.Web.Services元素到应用程序的配置文件中的配置元素中。还要指定可以读懂特定配置信息的WSE类。可以把下面的configSections添加到机器上的Machine.config或单独的Web.config中。
<configSections> <section name="microsoft.web.services" type="Microsoft.Web.Services.Configuration.WebServicesConfiguration, Microsoft.Web.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </configSections> |
在本例中,我们将使用Northwind数据库Employees表的一个修改版本来进行查询任务。因为PasswordProvider接口需要返回一个与UsernameToken对象的密码部分匹配的实际密码,所以通常,我们只需要使用WSE加密我们的用户名和密码,然后再通过网络传送给Web服务。
如果你在Solution Explorer中选中你的工程并在其上点击右键,你将看到在底部增加了一个新的菜单“WSE Settings”,你可以在其中设置所有重要的配置和其它使用WSE的配置:
这可让我们很容易的设置Password Provider Implementation(密码提供者实现)元素,Decryption Key Provider Implementation(解密钥提供者)元素,X.509 Certificate(X.509 证书)设置,甚至是我们希望使用的Binary Security Tokens(二进制安全令牌)。此外,其他的选项卡还可以配置用于WSE管道的输入输出过滤器,配置路由,启动诊断功能等等。虽然它不能做我们想做的每件事,但这是WSE易用化的一个良好的开端。
PasswordProvider安全元素是web.config中的<configuration>父元素的一个子元素,它告诉WSE你使用哪个类来实现PasswordProvider接口:
<microsoft.web.services> <security> <!-- NAMESPACE . CLASSNAME , ASSEMBLYNAME --> <passwordProvider type="WSESecurity.WSEPasswordProvider, WSESecurity" /> </security> </microsoft.web.services> |
让我们看看在本例中如何实现它:
namespace WSESecurity { public class WSEPasswordProvider : IPasswordProvider { public string GetPassword(UsernameToken token) { try { SqlConnection cn = new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["SqlConn"].ToString()); cn.Open(); SqlCommand cmd = new SqlCommand("SELECT Username, password from Employees where username =´" + token.Username + "´",cn); SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection); dr.Read(); return dr["password"].ToString(); } catch(Exception ex) { throw new Exception (ex.Message); } } } } |
上面我们给出的代码可以完全实现IPasswordProvider接口,通过用户名/密码来验证一个用户,当然了,还可以把它做得更复杂一些,这请读者们自己去完成。实际上,我们在编程的过程中基本没有写太多用户验证的代码,大部分工作都由WSE暗中处理了。
六、编写一个使用WS-Security的WebMethod
现在我们需要创建一个使用WS-Security的WebMethod。 这里,我实现了一个简单的方法,它运行Northwind数据库的CustOrderHist存储过程,接收一个字串UserID作为唯一的参数,并返回一个DataSet。如果调用Web服务的客户端可以通过消息级UsernameToken验证,那么就可以取回DataSet。如果不能通过验证的话,客户端将得到一个异常,告知它不能通过验证。WSE的优点在于你只要付出一点点劳动就可以了,大部分的工作已经由WSE在暗中为你完成了,所以你可以把大部分时间花费在构建Web服务的内容上,而不是为了构建一个安全的Web服务机制而疲于奔命。
[WebMethod] public DataSet CustOrderHist(string CustId) { // 只接受 SOAP格式的请求 SoapContext requestContext = HttpSoapContext.RequestContext; if(requestContext==null) { throw new ApplicationException("Non-SOAP request!"); } bool valid=false; try { foreach(SecurityToken tkn in requestContext.Security.Tokens) { if(tkn is UsernameToken) valid=true; } } catch(Exception ex) { throw new Exception( ex.Message + ": " + ex.InnerException.Message); } if (valid==false) throw new ApplicationException("Invalid or Missing Security Token."); SqlConnection cn; SqlDataAdapter da; DataSet ds; cn = new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["SqlConn"].ToString()); cn.Open(); da = new SqlDataAdapter("custorderHist ´" +CustId + "´", cn); ds = new DataSet(); da.Fill(ds, "CustOrderHist"); return ds; } |
使用上面的WebMethod,我们就可在服务器上实现验证用户名/密码的操作。WebMethod必须引用Microsoft.Web.Services和Microsoft.Web.Services.Security域名空间。现在,我们要构建一个ASP.NET客户端,这个客户端能够发送验证所需的SOAP头,并可调用我们的Web服务方法。
七、构建WSE ASP.NET客户端
对于客户端,WSE提供了继承于System.Web.Services.Protocols.SoapHttpClientProtocol类的Microsoft.Web.Services.WebServicesClientProtocol类。当你在Visual Studio.NET中选择“Add Web Reference”选项的时候,或者使用WSDL.exe程序创建基于WSDL的客户端代码时,你需要使用SoapHttpClientProtocol类。你可做的就是使用Visual Studio.NET中的“Add Web Reference”选项或者WSDL.exe程序为你的客户端生成代理类,然后把代理类从继承于SoapHttpClientProtocol改为继承于WebServicesClientProtocol。这样代理类就有了RequestSoapContext和ResponseSoapContext属性,你可以使用它们访问你发送或接收的WS-Security头。在C#工程中,如果你已经使用了“Add Web Reference”选项,你可以点击Solution Explorer中的“Show All Files”按钮,点击这个按钮就可在Solution Explorer的Web References结点中显示Reference.cs文件,让你可以编辑这个文件。
为了创建正确的UsernameToken和在消息级调用Web服务的代理方法,需要使用下面的代码:
private void Button1_Click(object sender, System.EventArgs e) { localhost.SecurityServiceWse wse=new localhost.SecurityServiceWse(); UsernameToken tkn = new UsernameToken(txtUsername.Text,txtPassword.Text,PasswordOption.SendHashed); wse.RequestSoapContext.Security.Tokens.Add (tkn); try { DataSet ds=wse.CustOrderHist(txtCustID.Text); DataGrid1.DataSource=ds; DataGrid1.DataBind(); } catch(Exception ex) { DataGrid1.Visible=false; lblMessages.Text=ex.Message; } } |
我们要做的就是从客户端的两个文本输入框txtUsername和txtPassword中取得输入字串,然后使用PasswordOption.SendHashed把它们结合起来创建一个有效的UserNameToken。当调用Web服务时,WSE SOAP扩展验证请求的一般格式,然后核对密码散列并从我们的PasswordProvider方法中取得的密码。如果两者匹配,我们就可调用Web服务方法,客户端返回数据集,显示在一个网格中。
我们现在已经创建了一个完整的使用WSE配合数据库验证SHA1摘要散列用户名/密码的Web服务,希望读者们能通过本文了解到使用WSE保证Web服务安全的基本措施和方法,并能在实际工作中合理的去应用。
在文章的最后,我们给出修改Northwind数据库Employees表的SQL脚本,给这个表添加了所需的username和password列,同时在这个表中插入了一条新纪录,其Firstname、Lastname、Username、Password和roles字段分别为“User”,“One”,“user1”,“pass1”和“user”。
USE NORTHWIND GO ALTER TABLE [dbo]. ADD [Username] [varchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL , [Password] [varchar] (100) COLLATE SQL_Latin1_General_CP1_CI_AS NULL , [roles] [varchar] (250) COLLATE SQL_Latin1_General_CP1_CI_AS NULL GO INSERT INTO EMPLOYEES (Firstname, Lastname,Username, [Password], roles) VALUES(´User´,´One´, ´user1´, ´pass1´, ´user´) GO |
转载于:https://www.cnblogs.com/zhouxiancai0128/archive/2006/10/26/540723.html
用WSE在Web服务中验证用户身份相关推荐
- Web服务中延时对QoE(体验质量)的影响
S. Egger等人在论文<WAITING TIMES IN QUALITY OF EXPERIENCE FOR WEB BASED SERVICES>中,研究了Web服务中延时对主观感受 ...
- 华为帐号服务升级,助力应用快速验证用户身份
目前,在电商.金融.社交等多种行业的APP中,"帐号+短信验证码"已成为验证用户身份的主流方式.短信验证具有易操作.安全性高.成本低的优势,在用户注册.登录.绑定手机号等许多场景中 ...
- shiro中的验证用户身份认证以及授权
目录 1.运用shiro进行用户身份认证: 1.1导入基于Shiro的数据库脚本 1.2.引入依赖(shiro-1.4.1) shiro-core shiro-web shiro-spring 1 ...
- 在 Web 应用中增加用户跟踪功能
http://www.ibm.com/developerworks/cn/web/wa-lo-usertrack/index.html 进行用户跟踪的重要性 随着 Web 应用的复杂化,用户在网站上的 ...
- SOA 设计原则和 Web 服务中的数据传输
SOA 被翻译为面向服务架构,它应该是创建从自治服务生成系统的一个架构方法,它的目的是更方便地进行集成. SOA是Web 服务的一个自然延伸,因为Web 服务只是解决了异构系统之间的互操作,并没有降低 ...
- python restful django_如何使用Django / Python从RESTful Web服务中使用XML?
我应该使用PyXML还是标准库中的内容? 解决方法: ElementTree是标准Python库的一部分. ElementTree是纯python,而cElementTree是更快的C实现: # Tr ...
- php socket传递cookie,PHP Websocket在测试中验证用户(传递会话cookie)
我试图测试一个方案,一方面,匿名用户应该立即断开与Websocket连接,另一方面,认证的用户应该留在websocket连接.第一种情况很容易通过使用下面的代码来测试.认证过程不工作. 对于会话存储, ...
- rest web服务_在WildFly的REST Web服务中与Jackson的双向关系
rest web服务 这是使用Jackson的REST Web服务中Java实体之间的双向关系的示例. 假设我们在两个实体Parent和Child之间存在双向关系. 使用MySQL工作台为这两个表生成 ...
- jax-rs jax-ws_通过JAX-WS Provider在Web服务中利用MOXy
jax-rs jax-ws 在以前的文章中,我演示了如何将EclipseLink JAXB(MOXy)直接集成到WebLogic(从12.1.1开始)和GlassFish(从3.1.2开始)的JAX- ...
最新文章
- Jquery 复选框全选与反选点击执行一次然后失效解决方案
- Extjs Window用法详解 2 打印具体应用
- linux下编译与运行,Linux操作系统驱动编译与运行是怎样的?
- linux 以下命令对中正确的是什么,2016年Linux认证模拟真题及答案
- THINKPHP3文件缓存管理
- JS 获取随机颜色值
- 【截至2006.12.31】毒霸反间谍可清除的部分流氓软件清单
- 在pfSense下设置GoDaddy动态DNS API记录
- Nginx+php+fastcgi的原理与关系
- AD命令获取计算机、用户相关信息
- 资源---2020考研---考研经验(总结:踏踏实实跟着汤家凤老师)
- 中心药库管理系统 v6.85 是什么
- C#从sqlite3中读数据到DataTable中报错 :System.Data.ConstraintException:“未能启用约束。一行或多行中包含违反非空、唯一或外键约束的值。
- Android开发使用百度地图定位误差过大
- ubuntu 22.0 不能调节屏幕亮度
- Java基础编程实现2-面向对象-类
- 雨水全自动浮动床过滤器
- 1月第2周业务风控关注 | 微信打击盗用他人的身份恶意注册,去年处理300多万例
- VMware设置共享文件夹之后Ubuntu中看不到怎么办?
- 小麦亩产一千八【数论】
热门文章
- 后台开发经典书籍--Redis深度历险:核心原理和应用实践
- muduo之TcpServer
- 共用体和枚举,写写意思下
- MySQL第7天:MySQL的架构介绍之存储引擎
- C#之windows桌面软件第十二课:电脑ADC值显示(上位机),记忆上次串口号,并用TrackBar控件显示ADC值
- MySQL笔记13:查询结果集
- JRebel for IntelliJ 热部署方法
- 新建仓库后第一次提交的问题
- JavaScript是如何工作的:使用MutationObserver跟踪DOM的变化
- “正则表达式”应当称为“规则表达式”