16.5 Web服务增强
很多Web服务会提供一组简单的数据,不过它们的实际价值在于集成应用,采用这种方法可以提供松耦合的服务来完成分布式处理。为了将Web服务从简单的方法调用增强为企业应用的集成部分,需要额外的一些特性。例如,如何保证Web服务安全,使得只有授权的用户才能访问?或者如何确保消息中的数据是安全的?再或者如何通过第三方网关完成消息路由?
其中一些问题可以用当前的框架来解决,例如,通常可以采用多种方法来完成安全和加密,这往往需要一些定制代码。Web服务增强(Web Service Enhancements,WSE)为这些问题提供了标准解决方案。WSE 3是行业标准规范的一个集合,包括WS-Security、WS-Addressing和WS-Policy。WSE 3的设计目标是:
q 轻松地构建安全的Web服务,提供安全的端到端消息传递,保证消息通过第三方路由时是安全的。
q 简化开发,能轻松地构建面向服务的应用。
q 提供未来验证和互操作性,使现有的Web服务能适应微软公司的长期远景,即基于服务的体系结构WCF(Windows Communication Foundation)。
对于小型网站,Web服务通常不需要太多WSE特性,但是在企业领域则完全不同。例如,考虑一个分层的系统,其中客户机发出一个Web服务调用,向一个支付服务器要求支付。这个消息包含支付的详细信息,它得到了适当的授权,然后这个消息转发到后端服务完成处理和日志记录。这里有两个要点,首先是通过支付服务器的消息路由。WSE允许消息路由通过不同的服务器到达其最终目标。后端服务接收到路由传递来的消息,但是它怎么知道这是一个合法的消息呢?或者它怎么知道支付服务器未受到破坏呢?因为有WSE,路由服务器可以向消息增加签名首部来保证消息的真实性。第二个问题是安全,你可能只想对消息的一部分加密,可能是不允许路由服务器访问的一些客户信息。基于线路的加密(如HTTPS)没有提供部分消息加密的功能,所以WSE会在协议级完成这一点。
WSE提供了5个主要的安全概要:
q 传输时用户名,使用传输层安全(如HTTPS)。客户认证基于一个授权库完成,如活动目录或SQL Server。
q 证书的用户名,使用一个X.509证书来保证安全,客户认证与“传输时用户名”的情况相同。
q 匿名证书,使用一个X.509证书来保证安全,客户是匿名的。
q 共有证书,客户和服务器交换用于保证数据安全的X.509证书。
q Kerberos,客户和服务器在一个Windows域中。
使用哪一个概要取决于你的具体情况,不过基于WSE 3的简单性和灵活性,使得编写安全的Web服务比使用以前版本的WSE要容易得多。
这本书并不打算深入地讨论WSE,所以有关的更多详细信息请访问MSDN WSE主页(http:// msdn.microsoft.com/webservices/webservices/building/wse/),还可以在这里下载WSE 3.0工具包。
WSE 3.0工具包
WSE 3.0工具包可以免费下载,作为.NET 2.0 Framework的一个免费插件,除了提供工具支持,还提供了基本功能。一旦安装,就可以从应用背景菜单使用WSE Settings 3.0选项来配置应用,或者手动编辑配置文件来完成配置。WSE 3.0的配置和使用以Web服务、客户和代理应用的策略为基础。策略定义了Web服务各组成部分所需的断言。
可以使用配置工具来设置策略(参见图16-6)。增加一个策略时,WSE安全设置向导允许你选择保护目标和安全方法(参见图16-7)。利用这个向导,还可以指定如何保护消息(参见图16-8)。此外还有一些步骤允许你显式地声明得到授权访问Web服务的用户和角色。
一旦定义了策略,接下来需要配置服务代码,首先在类上增加一个性质,如代码清单16-18所示。Policy性质的参数值就是策略名(使用图16-6中的Add按钮创建)。

图16-6 WSE策略配置

图16-7 选择应用类型和认证方法

图16-8 指定消息保护
代码清单16-18 向Web服务增加策略
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[Policy("usernameTokenSecurity")]
public class Service : System.Web.Services.WebService
默认情况下,认证根据活动目录完成,你在代码中所要做的只是取出安全token,并对用户授权,如代码清单16-19所示。GetUsernameToken从SOAP请求的安全token抽取出Username- Token(标识用户的一个WSE 3对象)。再用这个token检查从客户传递的用户名,如果它未能与合法的用户匹配,则抛出一个异常。这段代码同样可以检查用户是否属于一个合法的角色,或者以声明方式完成检查。由于WSE根据活动目录对用户授权,所以这是一个认证检查。
代码清单16-19 Web服务中授权用户
[WebMethod]
public string HelloWorld()
{
UsernameToken token = GetUsernameToken();
if (token.Username != @"domain\user")
throw new UnauthorizedAccessException("no way Jose");
return "Hello World";
}
private UsernameToken GetUsernameToken()
{
if (RequestSoapContext.Current == null)
throw new Exception("Only SOAP requests are permitted.");
// Make sure there's a token
if (RequestSoapContext.Current.Security.Tokens.Count == 0)
{
throw new SoapException(
"Missing security token",
SoapException.ClientFaultCode);
}
else
{
foreach (UsernameToken tok in
RequestSoapContext.Current.Security.Tokens)
return tok;
throw new Exception("UsernameToken not supplied");
}
}
如果希望根据另一个不同的用户库完成授权,如ASP.NET成员数据库,可以创建你自己的类(继承自UsernameTokenManager),并覆盖AuthenticateToken方法,这个方法为给定用户返回一个密码(参看代码清单16-20)。
代码清单16-20 根据ASP.NET成员库完成授权
public class AuthenticationManager : UsernameTokenManager
{
protected override string AuthenticateToken(UsernameToken token)
{
MembershipUser user = Membership.GetUser(token.Username);
return u.GetPassword();
}
}
客户应用上完成一个类似的配置过程,运行WSE安全向导来定义与服务器相同的规则。要调用受保护的Web服务,需要用用户凭证创建一个UsernameToken,并使用SetClient- Credential方法在代理上设置这个token。然后定义策略,并调用受保护的方法(代码清单16-21)。
代码清单16-21 调用受保护的Web服务
localhost.ServiceWse proxy = new localhost.ServiceWse();
UsernameToken token =
new UsernameToken(TextBox1.Text, TextBox2.Text,
PasswordOption.SendPlainText);
proxy.SetClientCredential(token);
proxy.SetPolicy("usernameTokenSecurity");
Label1.Text = proxy.HelloWorld();
显然这是最简单的情况,WSE还为消息签名和加密提供了配置和代码,同样很简单。有关的详细内容超出了本章的范畴,仅仅这一个内容就需要整本书来介绍。有关的详细信息,可以参考工具包提供的WSE 3.0 Quick-Start示例和文档,还可以参考 GotDotNet Web服务器安全项目(http://www.gotdotnet.com/codegallery/codegallery.aspx?id=67f659f6-9457-4860-80ff-0535dffed5e6),以及保护Web服务安全的Microsoft模式和实践指南,这个PDF可以从上述网站免费下载。







