A.8.4. 场景:ActAs WS-Trust
ActA
功能用于需要复合委派的情况。它通常用于多层系统中,其中的应用代表登录用户调用服务,或者服务代表原始调用者调用其他服务。
actionas
不仅仅是 RequestSecurityToken
(RST)中的新子元素。它在与 STS 协商令牌时提供有关原始调用者的额外信息。ActAs
元素通常采用令牌的形式,其身份声明(如名称、角色和授权代码)供客户端访问服务。
ActA
场景是基本 WS-Trust 场景的扩展。在本例中,ActAs
服务代表用户调用 ws-service
。对基本场景的代码仅作几处补充。添加了 ActAs
Web 服务提供商和回调处理程序。ActAs
Web 服务的 WSDL 采用了与 ws-provider
相同的安全策略。UsernameTokenCallbackHandler
是一个新实用程序,可为 ActAs
元素生成内容。最后,STS 中添加了一些代码来支持 ActAs
请求。
A.8.4.1. Web 服务提供商
本节详细介绍了已更改的基本 WS-Trust 场景中的 Web 服务元素,以满足 ActAs 示例
的需求。这些组件包括:
A.8.4.1.1. Web 服务提供商 WSDL
ActAs
Web 服务提供商的 WSDL 是 ws-provider 的 WSDL 的
克隆。wsp:Policy
部分是相同的。服务端点、target Namespace、port
Type
、绑定
名称和 服务
都有更改。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy" name="ActAsService" xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsaws="http://www.w3.org/2005/08/addressing" xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702" xmlns:t="http://docs.oasis-open.org/ws-sx/ws-trust/200512"> <types> <xsd:schema> <xsd:import namespace="http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy" schemaLocation="ActAsService_schema1.xsd"/> </xsd:schema> </types> <message name="sayHello"> <part name="parameters" element="tns:sayHello"/> </message> <message name="sayHelloResponse"> <part name="parameters" element="tns:sayHelloResponse"/> </message> <portType name="ActAsServiceIface"> <operation name="sayHello"> <input message="tns:sayHello"/> <output message="tns:sayHelloResponse"/> </operation> </portType> <binding name="ActAsServicePortBinding" type="tns:ActAsServiceIface"> <wsp:PolicyReference URI="#AsymmetricSAML2Policy" /> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/> <operation name="sayHello"> <soap:operation soapAction=""/> <input> <soap:body use="literal"/> <wsp:PolicyReference URI="#Input_Policy" /> </input> <output> <soap:body use="literal"/> <wsp:PolicyReference URI="#Output_Policy" /> </output> </operation> </binding> <service name="ActAsService"> <port name="ActAsServicePort" binding="tns:ActAsServicePortBinding"> <soap:address location="http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-actas/ActAsService"/> </port> </service> </definitions>
A.8.4.1.2. Web 服务提供商接口
ActAsServiceIface
Web 服务提供商界面类是一个简单的 Web 服务定义。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas; import javax.jws.WebMethod; import javax.jws.WebService; @WebService ( targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy" ) public interface ActAsServiceIface { @WebMethod String sayHello(); }
A.8.4.1.3. Web 服务提供商实施
ActAsServiceImpl
Web 服务提供商实施类是一个简单的 POJO。它使用标准的 WebService
注释来定义服务端点和两个 Apache WSS4J 注释(Ed pointProperties
和 EndpointProperty
),用于为 Apache CXF 运行时配置端点。提供的 WSS4J 配置信息用于 WSS4J 的 Crypto Merlin 实施。
ActAsServiceImpl
代表用户调用 ServiceImpl
。setupService
方法执行所需的配置设置。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas; import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; import org.apache.cxf.annotations.EndpointProperties; import org.apache.cxf.annotations.EndpointProperty; import org.apache.cxf.ws.security.SecurityConstants; import org.apache.cxf.ws.security.trust.STSClient; import org.jboss.test.ws.jaxws.samples.wsse.policy.trust.service.ServiceIface; import org.jboss.test.ws.jaxws.samples.wsse.policy.trust.shared.WSTrustAppUtils; import javax.jws.WebService; import javax.xml.namespace.QName; import javax.xml.ws.BindingProvider; import javax.xml.ws.Service; import java.net.MalformedURLException; import java.net.URL; import java.util.Map; @WebService ( portName = "ActAsServicePort", serviceName = "ActAsService", wsdlLocation = "WEB-INF/wsdl/ActAsService.wsdl", targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy", endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas.ActAsServiceIface" ) @EndpointProperties(value = { @EndpointProperty(key = "ws-security.signature.username", value = "myactaskey"), @EndpointProperty(key = "ws-security.signature.properties", value = "actasKeystore.properties"), @EndpointProperty(key = "ws-security.encryption.properties", value = "actasKeystore.properties"), @EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas.ActAsCallbackHandler") }) public class ActAsServiceImpl implements ActAsServiceIface { public String sayHello() { try { ServiceIface proxy = setupService(); return "ActAs " + proxy.sayHello(); } catch (MalformedURLException e) { e.printStackTrace(); } return null; } private ServiceIface setupService()throws MalformedURLException { ServiceIface proxy = null; Bus bus = BusFactory.newInstance().createBus(); try { BusFactory.setThreadDefaultBus(bus); final String serviceURL = "http://" + WSTrustAppUtils.getServerHost() + ":8080/jaxws-samples-wsse-policy-trust/SecurityService"; final QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy", "SecurityService"); final URL wsdlURL = new URL(serviceURL + "?wsdl"); Service service = Service.create(wsdlURL, serviceName); proxy = (ServiceIface) service.getPort(ServiceIface.class); Map<String, Object> ctx = ((BindingProvider) proxy).getRequestContext(); ctx.put(SecurityConstants.CALLBACK_HANDLER, new ActAsCallbackHandler()); ctx.put(SecurityConstants.SIGNATURE_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource("actasKeystore.properties" )); ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myactaskey" ); ctx.put(SecurityConstants.ENCRYPT_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource("../../META-INF/clientKeystore.properties" )); ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myservicekey"); STSClient stsClient = new STSClient(bus); Map<String, Object> props = stsClient.getProperties(); props.put(SecurityConstants.USERNAME, "alice"); props.put(SecurityConstants.ENCRYPT_USERNAME, "mystskey"); props.put(SecurityConstants.STS_TOKEN_USERNAME, "myactaskey" ); props.put(SecurityConstants.STS_TOKEN_PROPERTIES, Thread.currentThread().getContextClassLoader().getResource("actasKeystore.properties" )); props.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO, "true"); ctx.put(SecurityConstants.STS_CLIENT, stsClient); } finally { bus.shutdown(true); } return proxy; } }
A.8.4.1.4. ActAsCallbackHandler Class
ActAsCallbackHandler
是 WSS4J Crypto API 的回调处理程序。它用于在密钥存储中获取私钥的密码。此类使 Apache CXF 能够检索用于消息签名的用户名密码。此类已更新,以返回此服务的密码,即 myactaskey
和 ActAs
用户 alice
。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas; import org.jboss.wsf.stack.cxf.extensions.security.PasswordCallbackHandler; import java.util.HashMap; import java.util.Map; public class ActAsCallbackHandler extends PasswordCallbackHandler { public ActAsCallbackHandler() { super(getInitMap()); } private static Map<String, String> getInitMap() { Map<String, String> passwords = new HashMap<String, String>(); passwords.put("myactaskey", "aspass"); passwords.put("alice", "clarinet"); return passwords; } }
A.8.4.1.5. UsernameTokenCallbackHandler
RequestSecurityToken
的 ActAs
和 OnBeholdOf
子元素必须定义为 WSSE UsernameTokens
。此实用程序生成正确格式化的元素。
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.shared; import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.message.Message; import org.apache.cxf.ws.security.SecurityConstants; import org.apache.cxf.ws.security.trust.delegation.DelegationCallback; import org.apache.ws.security.WSConstants; import org.apache.ws.security.message.token.UsernameToken; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.Element; import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSSerializer; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import java.io.IOException; import java.util.Map; /** * A utility to provide the 3 different input parameter types for jaxws property * "ws-security.sts.token.act-as" and "ws-security.sts.token.on-behalf-of". * This implementation obtains a username and password via the jaxws property * "ws-security.username" and "ws-security.password" respectively, as defined * in SecurityConstants. It creates a wss UsernameToken to be used as the * delegation token. */ public class UsernameTokenCallbackHandler implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < callbacks.length; i++) { if (callbacks[i] instanceof DelegationCallback) { DelegationCallback callback = (DelegationCallback) callbacks[i]; Message message = callback.getCurrentMessage(); String username = (String)message.getContextualProperty(SecurityConstants.USERNAME); String password = (String)message.getContextualProperty(SecurityConstants.PASSWORD); if (username != null) { Node contentNode = message.getContent(Node.class); Document doc = null; if (contentNode != null) { doc = contentNode.getOwnerDocument(); } else { doc = DOMUtils.createDocument(); } UsernameToken usernameToken = createWSSEUsernameToken(username,password, doc); callback.setToken(usernameToken.getElement()); } } else { throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback"); } } } /** * Provide UsernameToken as a string. * @param ctx * @return */ public String getUsernameTokenString(Map<String, Object> ctx){ Document doc = DOMUtils.createDocument(); String result = null; String username = (String)ctx.get(SecurityConstants.USERNAME); String password = (String)ctx.get(SecurityConstants.PASSWORD); if (username != null) { UsernameToken usernameToken = createWSSEUsernameToken(username,password, doc); result = toString(usernameToken.getElement().getFirstChild().getParentNode()); } return result; } /** * * @param username * @param password * @return */ public String getUsernameTokenString(String username, String password){ Document doc = DOMUtils.createDocument(); String result = null; if (username != null) { UsernameToken usernameToken = createWSSEUsernameToken(username,password, doc); result = toString(usernameToken.getElement().getFirstChild().getParentNode()); } return result; } /** * Provide UsernameToken as a DOM Element. * @param ctx * @return */ public Element getUsernameTokenElement(Map<String, Object> ctx){ Document doc = DOMUtils.createDocument(); Element result = null; UsernameToken usernameToken = null; String username = (String)ctx.get(SecurityConstants.USERNAME); String password = (String)ctx.get(SecurityConstants.PASSWORD); if (username != null) { usernameToken = createWSSEUsernameToken(username,password, doc); result = usernameToken.getElement(); } return result; } /** * * @param username * @param password * @return */ public Element getUsernameTokenElement(String username, String password){ Document doc = DOMUtils.createDocument(); Element result = null; UsernameToken usernameToken = null; if (username != null) { usernameToken = createWSSEUsernameToken(username,password, doc); result = usernameToken.getElement(); } return result; } private UsernameToken createWSSEUsernameToken(String username, String password, Document doc) { UsernameToken usernameToken = new UsernameToken(true, doc, (password == null)? null: WSConstants.PASSWORD_TEXT); usernameToken.setName(username); usernameToken.addWSUNamespace(); usernameToken.addWSSENamespace(); usernameToken.setID("id-" + username); if (password != null){ usernameToken.setPassword(password); } return usernameToken; } private String toString(Node node) { String str = null; if (node != null) { DOMImplementationLS lsImpl = (DOMImplementationLS) node.getOwnerDocument().getImplementation().getFeature("LS", "3.0"); LSSerializer serializer = lsImpl.createLSSerializer(); serializer.getDomConfig().setParameter("xml-declaration", false); //by default its true, so set it to false to get String without xml-declaration str = serializer.writeToString(node); } return str; } }
A.8.4.1.6. crypto 属性和密钥存储文件
ActA
服务必须提供自己的凭据。创建必要的 actasKeystore.properties
属性文件和 actasstore.jks
密钥存储。
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin org.apache.ws.security.crypto.merlin.keystore.type=jks org.apache.ws.security.crypto.merlin.keystore.password=aapass org.apache.ws.security.crypto.merlin.keystore.alias=myactaskey org.apache.ws.security.crypto.merlin.keystore.file=actasstore.jks
A.8.4.1.7. 默认 MANIFEST.MF
此应用程序需要访问 org.jboss.ws.cxf.jbossws-cxf 中提供的 JBossWS 和 Apache CXF API。
在处理 ActAs
和 OnBehalfOf
扩展时,还需要 org.jboss.ws.cxf.sts
模块。dependency 语句指示服务器在部署时提供它们。
Manifest-Version: 1.0 Dependencies: org.jboss.ws.cxf.jbossws-cxf-client, org.jboss.ws.cxf.sts