dev2dev 首页 > 资源中心 > 技术文章
利用BEA WebLogic Workshop创建可靠的Web服务
下面的内容是Securing
Web Services with WS-Security: Demystifying WS-Security,
WS-Policy, SAML, XML Signature, and XML Encryption一书中的第10章,该书将于2004年5月由Sams出版社出版。
WebLogic Workshop集成开发环境(WebLogic Workshop Integrated
Development Environment)绑定了BEA WebLogic Server,它为Web服务的开发者提供了一个易于使用并且功能强大的工具。Workshop中的Web服务编写为Java Web服务(.jws)文件,这些文件没有什么特殊之处,它们只是一个Java文件,这种文件具有Javadoc标记格式的元数据。WebLogic Workshop 与WebLogic Server 中内建的WebLogic Security Framework 协同工作。WebLogic Security Framework 是一个功能强大、可扩展的安全性框架,它可以独立使用也可以集成于用户组织的安全机制环境中使用。WebLogic Security Framework
负责接收凭证,然后将凭证映射到一个用户,再将用户和一组角色相关联。在用WebLogic Workshop 开发.jws 程序时,用户关注的焦点是角色,而通常管理员更关注WebLogic Server 方面的安全策略(从凭证到用户再到角色)。
WebLogic Workshop支持三种类型的安全机制:HTTP传输安全机制、消息安全机制和基于角色的安全机制。从jws程序的观点来看,基于传输的安全机制和基于消息的安全机制属于外围安全机制。它们守卫着Web服务应用程序的大门。使用基于传输或基于消息的安全机制的结果是将一个用户与一个运行中的线程相关联,程序可以决定当前用户拥有的角色,这个结果对于Web服务程序来说是透明的。这就引出了基于角色的安全机制,它运行用户声明角色,一个用户要运行Web服务操作必须要和相应的角色相关联。
安全层概览
图1显示了一个从WebLogic Server运行环境到WebLogic Workshop运行环境的简化流程。图中显示了前面讨论过三种安全机制,WebLogic Server负责传输安全机制,WebLogic Workshop负责基于消息的安全机制和基于角色的安全机制。
当一条申请Workshop Web服务的消息进入WebLogic Server并且已经配置好传输安全机制的时候,WebLogic Server利用WebLogic Security Framework:1)建立SSL连接,2)收集并验证凭证(用户名/密码, X509证书等),3)将证书映射到一个用户,4)将用户映射到角色。
如果没有指定传输安全机制,或者即使指定了,接下来是由WebLogic Workshop处理的基于消息的安全机制。在消息安全机制中,我们可能接收到三种类型的安全元素。它们是令牌、XML加密技术和XML签名。WebLogic Workshop和WebLogic Security Framework一起:1)提取令牌(类似于凭证),验证令牌并且进行从凭证到用户再到角色的映射,2)进行XML加密技术的解密/加密操作,3)签署并且验证XML签名。对于基于传输和消息的安全机制来说,在用户的jws中得到的结果是一个用户(用JAAS术语来说,验证过的主体Subject)已经被绑定到一个线程,然后用户就可以使用基于角色的安全机制。尽管该图显示了基于传输和消息的安全机制,在WebLogic Workshop中更倾向于选择其中一种来保护Web服务。
在jws文件中,可以使用几种符号来增强基于角色的安全机制。利用基于角色的安全机制可以指定哪些角色能够访问Web服务里的功能。使用角色而不是具体用户使得管理员不用修改代码就可以从角色中删除用户。使用角色的最一般的方法是在jws文件头部或者在jws文件中的一个具体操作之上用@common:roles-allowed符号指定允许的角色。
图1:Web服务安全层
现在让我们简要说明Workshop是如何支持三种安全机制的:
传输层安全机制
使用传输层安全机制时有三种可用的互相联系的选项。第一个选项是使用单向SSL。它做两件事情:1)验证服务器,2)加密会话。第二个选项是使用BASIC验证。HTTP BASIC验证是基于用户名/密码的验证方式。指定BASIC验证时需要记住的一件事情是密码以明文的方式放在了HTTP头中。为了克服这个缺点,通常需要将BASIC验证和单向SSL组合使用。最后,功能极其强大但是也是非常复杂的选项是使用双向SSL,也称为客户验证方式。使用该选项后,客户端和服务器之间使用X509证书互相验证。
配置内置(inbound)传输安全机制的主要方法是修改在Web应用程序的WEB-INF 目录中的web.xml文件中的配置。查阅WebLogic Workshop
文档可以获得具体的配置信息。在客户验证模式下,必须在控制台选择该选项以通知WebLogic Server请求客户证书。
对于对外(outbound)传输安全机制,例如用户正在呼叫一个需要传输安全机制的Web服务,WebLogic Workshop为设置用户名/密码或客户证书信息提供了API。
消息层安全机制
WebLogic Workshop支持WS-Security安全头包含的所有三种类型的安全元素:令牌、XML加密元素和/或XML签名元素。
WebLogic Workshop的实现支持两种类型的令牌:表示一个用户名和一个密码的UserNameToken和表示一个X509证书的“X509”类型的BinarySecurityToken(我们称之为X509Token)。UserNameToken可以包含自己,然而在Workshop中,X509Token必须有一个XML签名。这样做的原因是X509证书包含的是一些公共信息,它本身不能说明发送者的任何身份信息。所以需要一个相应的XML签名来证明发送者控制了与X509令牌相关联的私钥。
在WebLogic Workshop中,如果指定使用加密技术,SOAP主体将与会话头加密在一起(若用户参与了一个会话)。
XML签名用于两个目的:其一,验证发送者的身份,通过上面提到的X509Token 来实现;其二,保证消息的完整性。即使消息的一个比特发生了变化,XML签名验证也会失败。在WebLogic Workshop中,如果指定使用签名,SOAP主体和会话头一起进行数字签名(若用户参与了一个会话)。
在WebLogic Workshop中配置WS-Security的方法是,用@jws:ws-security-policy-service
file="yourPolicy.xml"符号创建一个WS-Security策略文件并将它与Web服务相关联。若为进入Web服务的消息配置WS-Security策略就将该符号放在jws文件的头部。若为正在呼叫的Web服务配置WS-Security策略就应该将该符号放在Web服务控件的头部。
WS-Security符号指向一个xml文件,其中包含了用户想要应用的WS-Security策略。对于一个典型的同步Web服务呼叫的请求和响应消息来说,在WS-Security策略中有两个部分与其对应:wsSecurityIn和wsSecurityOut。在wsSecurityIn中声明用户在Web服务中期望的安全性。它回答下列问题:该Web服务需要令牌吗?需要何种类型的令牌?需要加密吗?如果确实需要加密,用于解密的私钥在哪里?需要签名吗?另一方面,wsSecurityOut回答下面的问题:是否需要给发送出去的消息添加一个令牌,如果需要,添加何种类型的令牌?消息需要加密吗?消息是否需要签名,如果需要,应该使用哪个私钥来创建XML签名?
基于角色的安全机制
基于传输和消息的安全机制的结果是一个具有一组相关角色的经验证的主体。这些角色是用户jws文件中使用的主要安全机制。符号@common:security用于基于角色的安全机制,可用的属性有roles-allowed、roles-referenced和run-as。也可以使用API context.isCallerInRole("role"),其中context是类weblogic.jws.JwsContext 的一个实例,其中的代码可以对一个具体的角色进行测试。
最常用的符号是roles-allowed。正如我们所期待的,如果使用了roles-allowed,只有属于指定角色的用户才可以执行请求的操作。roles-allowed符号可以用于类的层次(高于类的声明)或者用于操作的层次。调用操作时,类层次的roles-allowed 和操作层次的roles-allowed 是结合在一起的。这样在操作层次上添加roles-allowed扩展了能够访问某个应用程序的角色数目。
@common:security roles-referenced属性用于提升用户在jws代码中使用context.isCallerInRole("role")访问过的角色。这样做的目的是使得用户在代码中用过的角色可以映射到适当的目标环境中。这样用户可以选择当前应用程序范围中可用但是在发布环境中具有不同名字的角色。
@common:security run-as属性用来在用户的jws代码中改变身份,这样在呼叫时就可以呈现新的身份。例如,如果用户有一个数据库或者ejb需要具有system角色,可以使用run-as来改变jws的对外(outbound)身份。
现在让我们通过一个例子来体会如何在WebLogic Workshop
中使用这些安全机制。
浏览一个WebLogic Workshop Web服务例子
我们将要创建两个Web服务,POClient.jws和POService.jws,其中PO是Purchase Order(定单)的首字母缩写。正如名字所暗示的那样,POClient.jws传递数据给POService.jws,POService.jws创建定单并返回一个定单号。首先,我们将通过应用传输安全机制保护POService.jws Web服务,使用单向SSL和HTTP BASIC验证,然后学习如何使用双向SSL。然后我们应用消息层安全机制,利用WebLogic Workshop
中实现的WS-Security来测试其特征并分析输出结果。最后,我们使用基于角色的安全机制授权对POService.jws 的访问。下面是具体步骤:
上图描述了例子中使用的安全机制架构
1.
在Workshop 中创建一个新应用程序
a.
在Workshop中选择File –> New –> Application菜单选项创建一个新的应用程序。Workshop 应用程序只是一个扩展的J2EE(Java 2 Enterprise
Edition)应用程序,也就是说是一个Enterprise Archive (EAR)文件。
b.
选择Default Application 选项。点击Browse按钮浏览想要创建应用程序的目录,比如C:\apps。用户也可以在Directory文本框输入自己的目录。然后在Name文本框输入应用程序的名字,比如SecureApp。
程序中默认创建了一个名为SecureAppWeb的Web项目。在WebLogic Workshop中,一个Web项目是一个J2EE Web应用程序。
2.
a.
在SecureAppWeb
项目上单击鼠标右键并选择New –> Folder选项新建一个文件夹。
b.
将新建的文件夹命名为“securePackage”
3.
a.
鼠标右键单击securePackage文件夹并选择New –> Web Service选项在securePackage 文件夹中创建一个新的Web服务。
b.
将新建的Web服务命名为POService.jws
4.
一个Web服务在Workshop中有两个视图(View),Design View和Source View。Design View为Web服务提供一个图形化的展示,Source View 用来查看源代码。用户可以在两个视图之间切换,方法是选择Workshop IDE中央窗口底部的适当的标签或使用View 菜单中的选项。
a.
在下图所示的Design View 中单击鼠标右键为Web服务添加一个方法。要添加一个方法,也可以选择Insert –> Method 菜单选项或者从IDE左下角的Pallette 窗口中将Method 图标拖到Web服务的Design View中。
b.
将新添加的方法重命名为submitPO
5.
切换到Source View。在Source View中可以看到下面的代码:
package securePackage;
public class POService implements com.bea.jws.WebService
{
/**
* @common:operation
*/
public int submitPO()
{
}
}
|
注意submitPO 方法上方的javadoc 符号。Workshop运行程序使用这些符号来产生Web服务的底层支持,例如SOAP –> Java信号编组。正如我们所看到的那样,我们在Web服务的Design View中增加一个方法时系统自动生成这些代码。
6.
在POService.jws 中创建一个静态内部类POBean,并且为submitPO 方法提供一个实现。
可以将下面的代码复制到文件中。
POService.jws
package securePackage;
public class POService implements com.bea.jws.WebService
{
public static class POBean
{
public POBean(String itemName, int itemQuantity,
double itemPrice)
{
this.itemName = itemName;
this.itemQuantity = itemQuantity;
this.itemPrice = itemPrice;
}
public POBean(){};
public String itemName;
public int itemQuantity;
public double itemPrice;
}
/**
* This is an exposed method that users will
be able to invoke.
* @common:operation
*/
public int submitPO(POBean poBean)
{
// Code for entering the purchase
order in the database
return 1;
}
}
|
传输安全机制
现在让我们为Web服务实现传输层安全机制和基本HTTP验证。
1.
首先定位配置描述符,web.xml和weblogic.xml,它们位于SecureAppWeb 项目的WEB-INF 目录中。在一个J2EE兼容的Web应用程序中,如我们的SecureAppWeb 项目,用两个配置描述符viz. web.xml和weblogic.xml来指定详细的配置属性,底层的WebLogic Server在配置的时候读取并解释之。其中包含了和安全机制相关的属性,如我们的例子中要使用的SSL和HTTP验证。
a.
双击web.xml,它将出现在中央窗口中。将下面的文本粘贴到最后一个元素</web-app>之上为其增加安全限制:
<security-constraint>
<display-name>Security Constraints</display-name>
<web-resource-collection>
<web-resource-name>
Secure Resources
</web-resource-name>
<url-pattern>
/securePackage/POService.jws
</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>GoodRole</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>
CONFIDENTIAL
</transport-guarantee>
</user-data-constraint>
</security-constraint>
<security-role>
<description>Role description</description>
<role-name>GoodRole</role-name>
</security-role>
|
b.
这里我们已经增加了四个重要属性:
§
<url-pattern> 确保服务得到基于URL模式的保护。
§
<auth-constraint> 说明访问受限的安全角色。我们需要给这个角色添加用户并为他们提供对可靠服务的访问权限。
§
<transport-guarantee> 元素通过指定"CONFIDENTIAL"或"INTEGRAL"中的一个值来限制对HTTP协议的访问。
§
<security-role> 元素定义安全角色
c.
在weblogic.xml中,通过在</weblogic-web-app>元素之前增加下列元素将角色GoodRole映射到一个用户。这时候,底层WebLogic Server实例下使用的该用户可能存在也可能不存在。
<security-role-assignment>
<role-name>GoodRole</role-name>
<principal-name>GoodUser</principal-name>
</security-role-assignment>
|
2.
如果用户“GoodUser”在当前部署该应用程序的服务器上不存在,我们必须创建一个。
a.
要添加一个用户,我们需要启动Workshop IDE
指向的服务器。默认情况下,IDE指向<BEA-HOME>/weblogic81/samples/domains/workshop 域,其中<BEA-HOME>是用户在安装WebLogic Platform时选择作为“BEA Home Directory”的目录。
选择Tools –> WebLogic
Server –> Start WebLogic Server选项来启动服务器。
出现下面的窗口指示服务器启动进程的进度。
b.
服务器启动后,单击Tools –> WebLogic Server –> WebLogic Console菜单选项打开WebLogic server 控制台。默认的用户名和密码分别是"weblogic"和"weblogic"。
c.
在下面显示的WebLogic server控制台的Security –> Realm –> myrealm –> Users 屏幕上单击"Configure a New User"连接为树型控件默认域“myrealm”添加一个用户:
d.
将用户命名为“GoodUser”然后任意输入一个密码,比如“weblogic”。密码的默认最小长度为8。
3.
在POService.jws Web服务上单击鼠标右键,生成一个服务控件(.jcx)文件。
控件文件是一个Workshop web服务的客户端代理。生成文件的默认文件名是POServiceControl.jcx。控件文件默认包含下列符号:
@jc:location http-url="POService.jws"
|
该值是POService.jws
相对于服务控件文件的终点URL。因为该服务和控件文件在同一个目录里,所以相应的URL正好是文件名。要配置控件代理来向服务发送一个HTTP请求,需要修改绝对HTTP URL的相对URL,如下所示:
/**
* @jc:location http-url=
"https://localhost:7002/SecureAppWeb/securePackage/POService.jws"
*/
|
用户第一次修改文件时,Workshop会弹出一个警告:
单击OK继续。这只是表明下一次修改并保存.jws 文件时,.jcx 文件不会自动更新。任何时候在JWS单击鼠标右键都可以重新生成控件文件。
控件文件看起来如下所示,接下来的内容是WSDL:
package securePackage;
/**
* @jc:location http-url="https://localhost:7002/SecureAppWeb
/securePackage/POService.jws" jms-url="POService.jws"
* @jc:wsdl file="#POServiceWsdl"
*/
public interface POServiceControl extends
com.bea.control.ControlExtension,
com.bea.control.ServiceControl
{
public static class POBean
implements java.io.Serializable
{
public java.lang.String itemName;
public int itemQuantity;
public double itemPrice;
}
/**
* This is an exposed method that users will be able to invoke.
* @jc:protocol form-post="false" form-get="false"
*/
public int submitPO (POBean poBean);
static final long serialVersionUID = 1L;
}
|
4.
如果注意到WSDL中作为<soap:address>元素中的位置属性值的服务终点URL,我们会发现它默认指定了HTTP协议:
<service name="POService">
<port name="POServiceSoap" binding="s0:POServiceSoap">
<soap:address
location="http://localhost:7001
/securePackage/POService.jws"/>
</port>
</service>
|
要确保在WSDL文件中为POService 的终点URL指定HTTP协议,将下列文本复制到WEB-INF 目录中的wlw-config.xml 文件中:
<wlw-config xmlns="http://www.bea.com/wlw/runtime/core/config">
<protocol>http</protocol>
<hostname>localhost</hostname>
<http-port>7001</http-port>
<https-port>7002</https-port>
<jws>
<class-name>securePackage.POService</class-name>
<protocol>https</protocol>
</jws>
</wlw-config>
|
5.
现在创建另外一个Web服务POClient.jws,它将会调用第一个服务。
a.
在securePackage文件夹上单击鼠标右键选择New –> Web Service
选项创建服务,重新命名为POClient.jws。
b.
将POServiceControl.jcx拖放到POClient.jws file文件的Design View 中。
c.
在默认位于IDE屏幕的右下方的Data Palette窗口中,可以看到控件显示的方法和回调函数。拖放submitPO()方法到屏幕中部的Design View窗口。
6.
现在切换到Source View窗口,可以看到Workshop已经自动生成了下列代码:
package securePackage;
public class POClient implements com.bea.jws.WebService
{
/**
* @common:control
*/
private securePackage.POServiceControl pOServiceControl;
static final long serialVersionUID = 1L;
/**
* @common:operation
*/
public int submitPO(securePackage.
POServiceControl.POBean poBean)
{
return pOServiceControl.submitPO(poBean);
}
}
|
7.
现在在控件文件中做如下修改:
a.
创建POBean的一个实例来传递控件的submitPO方法。
b.
修改submitPO方法的签名以接受用户名和密码。
c.
在控件中设置用户名和密码。将用户名和密码进行加密然后作为HTTP头传送到服务。这里使用了HTTP BASIC验证。
这里是服务的代码:
POClient.jws
package securePackage;
import securePackage.POServiceControl.POBean;
public class POClient implements com.bea.jws.WebService
{
/**
* @common:control
*/
private securePackage.POServiceControl pOServiceControl;
static final long serialVersionUID = 1L;
/**
* @common:operation
*/
public int submitPO(String username, String password)
{
POBean poBean = new POBean();
poBean.itemName = "Metal sheets";
poBean.itemQuantity = 1;
poBean.itemPrice = 100.00;
// Code for specifying the username and password
pOServiceControl.setUsername(username);
pOServiceControl.setPassword(password);
return pOServiceControl.submitPO(poBean);
}
}
|
8.
单击IDE上的Start 按钮(IDE上部工具栏中的蓝色小三角型图标)或从菜单中选择Debug –> Start选项创建并运行Web服务。这是弹出了Workshop内置的浏览器。在“Test Form”标签中可以看到两个可以输入username和password的文本框。
a.
输入一个错误的用户名和密码,然后点击submitPO 按钮来激活该方法。验证失败并返回一个SOAP故障,并保存在日志中:
在消息日志标签中有两个子标签。点击submitPO标签并且滚动到底部可以看到包含如下字符串的消息
"String:Response: '401: Unauthorized xxx' for url: 'https://GoodUser:weblogic1@localhost:7002/Secure
AppWeb/securePackage/POService.jws'"
b.
现在输入“GoodUser”作为用户名和它的相应的密码。可以成功的访问服务并且显示如下的日志。该日志显示了请求和响应。
本例显示了使用BASIC验证的单向SSL的用法。类似于在控件文件中设置用户名和密码的API,Workshop也为双向SSL提供了用于设置证书API,在双向SSL中客户验证也使用证书。下面是在控件上设置客户端证书的一个例子:
pOServiceControl.useClientKeySSL( true );
pOServiceControl.setClientKeyAlias( "mykey" );
pOServiceControl.setClientKeyPassword( "password" );
pOServiceControl.setClientKeyStoreLocation
("c:/bea/weblogi81/server/lib/mycacerts");
pOServiceControl.setClientKeyStorePassword( "password" );
|
基于消息的安全机制
WebLogic Workshop 支持WS-Security 提供的全部三类安全元素:令牌、XML加密技术和XML签名。
正如我们在引言中讨论过的,客户端(即服务控件)和服务端(即Web服务)应该引用WS-Security 策略(*.wsse)文件来使用WS-Security。我们在securePackage文件夹中添加两个WS-Security 策略文件,一个是用于POService.jws 文件,另一个用于POServiceControl.jcx。我们浏览一下具体的步骤:
3.
1.右击securePackage文件夹并且选择“Other File Types”选项,然后选择Services –> WS-Security
Policy file选项,在securePackage文件夹中创建一个WS-Security 策略文件。
4.
2.将文件命名为POServiceSecurityPolicy.wsse
在文件中默认生成如下代码:
<?xml version="1.0" ?>
<wsSecurityPolicy xsi:schemaLocation="WSSecurity-policy.xsd"
xmlns="http://www.bea.com/2003/03/wsse/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
</wsSecurityPolicy>
|
5.
3.将下列代码粘贴到文件中:
<wsSecurityPolicy xsi:schemaLocation="WSSecurity-policy.xsd"
xmlns="http://www.bea.com/2003/03/wsse/config"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsSecurityIn>
<!--
Incoming SOAP message must be accompanied by a valid username
and password.
-->
<token tokenType="username"/>
<!--
Incoming SOAP messages must be encrypted with mycompany.jws's
public key. The alias and password to access the mycompany.jws's
decrypting private key in the keystore are provided by
the <decryptionKey> element below.
-->
<encryptionRequired>
<decryptionKey>
<alias>mycompany</alias>
<password>password</password>
</decryptionKey>
</encryptionRequired>
<!--
Incoming SOAP messages must be digitally signed with the sender's
private key.
The sender's public key is used to validate the signature.
-->
<signatureRequired>true</signatureRequired>
</wsSecurityIn>
<wsSecurityOut>
<!--
Accompany the SOAP message with a valid username and password
-->
<userNameToken>
<userName>GoodUser</userName>
<password type="TEXT">weblogic</password>
</userNameToken>
<!--
Encrypt the SOAP message with the recipient's (Client.jws) public key.
Only the recipient's private key can decrypt it.
Ensures the confidentiality of the SOAP message.
(This process requires that the sender's keystore already contains
a digital certificate containing the recients public key.)
-->
<encryption>
<encryptionKey>
<alias>client1</alias>
</encryptionKey>
</encryption>
<!--
Sign the SOAP message with the sender's (MyCompany.jws) private key.
Only the sender's public key can validate the signature.
Ensures the authenticity of the sender, i.e., that the sender is
in fact the source of the SOAP message.
-->
<signatureKey>
<alias>mycompany</alias>
<password>password</password>
</signatureKey>
</wsSecurityOut>
<!--
Look for the mycompany.jks keystore in the default location, the server domain
root, in this case, BEA_HOME\weblogic81\samples\domains\workshop.
-->
<keyStore>
<keyStoreLocation>samples_mycompany.jks</keyStoreLocation>
<keyStorePassword>password</keyStorePassword>
</keyStore>
</wsSecurityPolicy>
|
为在策略文件中添加注释,这里是各种元素的简单解释:
§ &nb |