API client WS-Trust
L'interface de programme d'application (API) client WS-Trust contient les classes WSSTrustClient et WSSTrustClientValidateResult, ainsi que d'autres classes d'utilitaires de configuration. La classe WSSTrustClient contient des fonctions d'aide programmables destinées à l'envoi de demandes SOAP WS-Trust au service STS externe spécifié, pour que celui-ci puisse émettre ou valider une ou plusieurs assertions SAML et d'autres types de jetons de sécurité.
Généralités
WebSphere Application Server inclut la fonction WS-Trust,implémentée via la classe WSSTrustClient, qui envoie les demandes WS-Trust SOAP à un service STS (Security Token Service) externe défini. En utilisant ces demandes de jeton, le service de jeton de sécurité peut émettre ou valider une ou plusieurs assertions SAML ou d'autres types de jetons de sécurité. La classe WSSTrustClient est compatible avec les spécifications OASIS WS-Trust version 1.3 et 1.2. En outre, la fonction prend également en charge les spécifications SOAP versions 1.1 et 1.2.
Le modèle de code qui suit illustre la façon dont un client de services Web utilise l'API WSSTrustClient pour demander un jeton SAML bearer. Dans les explications qui précèdent le modèle de code, les termes jeton SAML et assertion SAML sont utilisés de façon interchangeable.
La classe WSSTrustClient
Vous pouvez copier le modèle de code dans une application d'assemblage, par exemple Rational Application Developer, et commencer à l'utiliser après avoir effectué les étapes de configuration. La classe WSSTrustClient, utilisée conjointement avec d'autres API SAML, vous permet de générer des fonctions SAML répondant à vos besoins. Reportez-vous à la documentation Java sur les API SAML pour de plus amples informations.
La classe WSSTrustClient est une classe abstraite qui dispose de deux implémentations concrètes : une implémentation WS-Trust version 1.3 et une implémentation WS-Trust version 1.2. A la ligne 50 du modèle, le code client de services Web SAMLWSTrustClientExample appelle la méthode WSSTrustClient.getInstance(ProviderConfig) pour récupérer l'implémentation WS-Trust version 1.3. La méthode getInstance() utilise un seul objet ProviderConfig, qui définit les données de configuration correspondant à l'émetteur du jeton SAML. Un objet ProviderConfig est également instancié à la ligne 32 du modèle de code. Le code client envoie des messages de demande WS-Trust version 1.3 au noeud final d'un service cible de jeton de sécurité. Dans le modèle, le noeud final est https://MyCompany/Trust/13/UsernameMixed. Pour utiliser le modèle, remplaces ce noeud par celui du service de jeton de sécurité que vous prévoyez d'utiliser.
Le support SAML de l'API WSS complète les interfaces com.ibm.websphere.wssecurity.wssapi.token.SAMLTokenFactory et com.ibm.websphere.wssecurity.wssapi.trust.WSSTrustClient. Les SAMLTokens générés par un appel à la méthode com.ibm.websphere.wssecurity.wssapi.WSSFactory newSecurityToken() peuvent être traités par les interfaces de programmation SAMLTokenFactory et WSSTrustClient. Inversement, les SAMLTokens générés par SAMLTokenFactory ou renvoyés par WSSTrustClient peuvent être utilisés dans l'API WSS. Le choix de l'API à utiliser dans votre application dépend de vos besoins spécifiques. Le support SAML de l'API WSS est intégré et autonome en ce sens qu'il fournit une fonctionnalité équivalente à celle des interfaces SAMLTokenFactory et WSSTrustClient sur le plan des applications client de services Web. L'interface SAMLTokenFactory dispose de fonctions supplémentaires pour valider les SAMLTokens et créer le sujet JAAS qui représente les SAMLTokens authentifiés. Cette validation est utile du côté du fournisseur de services Web. Lorsque vous développez des applications appelées à consommer des SAMLTokens, l'interface de programmation SAMLTokenFactory est celle qui convient le mieux à vos besoins.
Exemple : Code d'un client de services Web utilisant la classe WSSTrustClient
1. package sample;
2.
3. import com.ibm.websphere.wssecurity.wssapi.WSSException;
4. import com.ibm.websphere.wssecurity.wssapi.token.SecurityToken;
5. import com.ibm.websphere.wssecurity.wssapi.trust.WSSTrustClient;
6. import com.ibm.websphere.wssecurity.wssapi.token.SAMLToken;
7. import com.ibm.websphere.wssecurity.wssapi.XMLStructure;
8.
9.
10. import com.ibm.wsspi.wssecurity.core.token.config.RequesterConfiguration;
11. import com.ibm.wsspi.wssecurity.core.token.config.WSSConstants.Namespace;
12. import com.ibm.wsspi.wssecurity.core.token.config.WSSConstants.TokenType;
13. import com.ibm.wsspi.wssecurity.core.token.config.WSSConstants.WST13;
14. import com.ibm.wsspi.wssecurity.trust.config.ProviderConfig;
15. import com.ibm.wsspi.wssecurity.trust.config.RequesterConfig;
16. import com.ibm.wsspi.wssecurity.wssapi.OMStructure;
17.
18. import org.apache.axiom.om.OMElement;
19. import org.apache.axis2.util.XMLPrettyPrinter;
20.
21. import java.util.List;
22. import java.io.ByteArrayOutputStream;
23. import java.io.InputStream;
24. import java.io.BufferedReader;
25. import java.io.InputStreamReader;
26. import java.io.IOException;
27.
28. public class WSSTrustClientExample {
29.
30. public static void main(String[] args) {
31. try {
32. ProviderConfig providerConfig = WSSTrustClient.newProviderConfig(Namespace.WST13, https://MyCompany.com/Trust/13/UsernameMixed );
33.
34. showProviderConfigDefaultValue(providerConfig);
35.
36. providerConfig.setPolicySetName("Username WSHTTPS default");
37. providerConfig.setBindingName("SamlTCSample");
38. providerConfig.setBindingScope("domain");
39.
40.
41. RequesterConfig requesterConfig = WSSTrustClient.newRequesterConfig(Namespace.WST13);
42.
43. showRequestConfigDefaultValue(requesterConfig);
44.
45. requesterConfig.put(RequesterConfiguration.RSTT.APPLIESTO_ADDRESS, "https://user.MyCompany:9443/WSSampleSei/EchoService12");
46. requesterConfig.put(RequesterConfiguration.RSTT.TOKENTYPE, TokenType.SAML11);
47. requesterConfig.put(RequesterConfiguration.RSTT.KEYTYPE, WST13.KEYTYPE_BEARER);
48. requesterConfig.setSOAPNamespace(Namespace.SOAP12);
49.
50. WSSTrustClient client = WSSTrustClient.getInstance(providerConfig);
51. List<SecurityToken> securityTokens = client.issue(providerConfig, requesterConfig);
52.
53. // Traiter le jeton SAML
54. if (securityTokens != null && !securityTokens.isEmpty()) {
55. System.out.println("Number of tokens returned = " + securityTokens.size());
56. SecurityToken token = securityTokens.get(0);
57. if (token instanceof SAMLToken) {
58. showSAMLToken((SAMLToken)token);
59. } else {
60. System.out.println("Returned token is not an SAMLToken");
61. }
62. } else {
63. System.out.println("No securityToken obtained.");
64. }
65.
66. } catch (SoapSecurityException ex) {
67. System.out.println("Caught exception: " + ex.getMessage());
68. ex.printStackTrace();
69. }
70. }
71.
72. private static void showProviderConfigDefaultValue(ProviderConfig providerConfig) {
73. System.out.println("providerConfig.getApplicationName() = " + providerConfig.getApplicationName());
74. System.out.println("providerConfig.getBindingName() = " + providerConfig.getBindingName());
75. System.out.println("ProviderConfig.getBindingScope() = " + providerConfig.getBindingScope());
76. System.out.println("providerConfig.getIssuerURI() = " + providerConfig.getIssuerURI());
77.
78. System.out.println("providerConfig.getPolicySetName() = " + providerConfig.getPolicySetName());
79. System.out.println("ProviderConfig.getPortName() = " + providerConfig.getPortName());
80. System.out.println("providerConfig.getProvider() = " + providerConfig.getProvider());
81. System.out.println("ProviderConfig.getServiceName() = " + providerConfig.getServiceName());
82. System.out.println("providerConfig.getWSTrustNamespace() = " + providerConfig.getWSTrustNamespace());
83. System.out.println("ProviderConfig.toString() = " + providerConfig.toString());
84. }
85.
86. private static void showRequestConfigDefaultValue(RequesterConfig requesterConfig) {
87. System.out.println("requesterConfig.getRSTTProperties() = " + requesterConfig.getRSTTProperties());
88. System.out.println("requesterConfig.getSecondaryParameters() = " + requesterConfig.getSecondaryParameters());
89. System.out.println("requesterConfig.getSOAPNamespace() = " + requesterConfig.getSOAPNamespace());
90. System.out.println("requesterConfig.getWSAddressingNamespace() = " + requesterConfig.getWSAddressingNamespace());
91.
92. System.out.println("requesterConfig.getMessageID() = " + requesterConfig.getMessageID());
93. System.out.println("requesterConfig.toString() = " + requesterConfig.toString());
94. }
95.
96. private static void showSAMLToken(SAMLToken samlToken){
97. System.out.println("samlToken.getAssertionQName() = " + samlToken.getAssertionQName());
98. System.out.println("samlToken.getAudienceRestriction() = " + samlToken.getAudienceRestriction());
99. System.out.println("samlToken.getAuthenticationMethod() = " + samlToken.getAuthenticationMethod());
100. System.out.println("samlToken.getConfirmationMethod() = " + samlToken.getConfirmationMethod());
101. System.out.println("samlToken.getId() = " + samlToken.getId());
102. System.out.println("samlToken.getKeyIdentifier() = " + samlToken.getKeyIdentifier());
103. System.out.println("samlToken.getKeyIdentifierEncodingType() = " + samlToken.getKeyIdentifierEncodingType());
104. System.out.println("samlToken.getKeyIdentifierValueType() = " + samlToken.getKeyIdentifierValueType());
105. System.out.println("samlToken.getKeyName() = " + samlToken.getKeyName());
106. System.out.println("samlToken.getPrincipal() = " + samlToken.getPrincipal());
107. System.out.println("samlToken.getProperties() = " + samlToken.getProperties());
108. System.out.println("samlToken.getReferenceURI() = " + samlToken.getReferenceURI());
109. System.out.println("samlToken.getSAMLAttributes() = " + samlToken.getSAMLAttributes());
110. System.out.println("samlToken.getSamlCreated() = " + samlToken.getSamlCreated());
111. System.out.println("samlToken.getSamlExpires() = " + samlToken.getSamlExpires());
112. System.out.println("samlToken.getSamlID() = " + samlToken.getSamlID());
113. System.out.println("samlToken.getSAMLIssuerName() = " + samlToken.getSAMLIssuerName());
114. System.out.println("samlToken.getSAMLNameID() = " + samlToken.getSAMLNameID());
115. System.out.println("samlToken.getStringAttributes() = " + samlToken.getStringAttributes());
116. System.out.println("samlToken.getSubjectDNS() = " + samlToken.getSubjectDNS());
117. System.out.println("samlToken.getSubjectIPAddress() = " + samlToken.getSubjectIPAddress());
118. System.out.println("samlToken.getThumbprint() = " + samlToken.getThumbprint());
119. System.out.println("samlToken.getThumbprintEncodingType() = " + samlToken.getThumbprintEncodingType());
120. System.out.println("samlToken.getThumbprintValueType() = " + samlToken.getThumbprintValueType());
121. System.out.println("samlToken.getTokenQname() = " + samlToken.getTokenQname());
122. System.out.println("samlToken.getValueType() = " + samlToken.getValueType());
123.
124. XMLStructure samlXmlStructure = samlToken.getXML();
125. if (samlXmlStructure != null && samlXmlStructure instanceof OMStructure) {
126. OMStructure samlOMStructure = (OMStructure) samlXmlStructure;
127. System.out.println("((OMStructure)samlToken.getXML()).getNode()formatted = " + formatXML(samlOMStructure.getNode()));
128. }
129.
130. try {
131. InputStream is = samlToken.getXMLInputStream();
132. if (is != null) {
133. try {
134. BufferedReader reader = new BufferedReader(new InputStreamReader(is));
135. StringBuilder sb = new StringBuilder();
136. String line = null;
137. while ((line = reader.readLine()) != null) {
138. sb.append(line + "\n");
139. }
140. System.out.println(sb.toString());
141. } catch (Exception ex) {
142. System.out.println("Caught exception reading from InputStream: " + ex.getMessage());
143. ex.printStackTrace();
144. } finally {
145. try {
146. is.close();
147. } catch (IOException e) {
148. e.printStackTrace();
149. }
150. }
151. }
152. } catch (WSSException wex) {
153. System.out.println("Caught exception getXMLInputStream(): " + wex.getMessage());
154. wex.printStackTrace();
155. }
156. }
157.
158. private static String formatXML(OMElement omInput) {
159. ByteArrayOutputStream out = new ByteArrayOutputStream();
160. String output = "";
161.
162. try {
163. XMLPrettyPrinter.prettify(omInput, out);
164. output = out.toString();
165. } catch (Throwable e) {
166. try {
167. output = omInput.toString();
168. } catch (Throwable e2) {
169. System.out.println("Caught exception: " + e2.getMessage());
170. e2.printStackTrace();
171. }
172. }
173. return output;
174. }
175.
176. }
Prise en charge des ensembles de règles et des liaisons par la classe WSSTrustClient
La fonction client WS-Trust prend en charge l'utilisation à la fois des liaisons spécifiques aux applications et des liaisons générales avec l'ensemble de règles et les documents de liaisons des clients sécurisés. En outre, les liaisons générales et les liaisons par défaut sont prises en charge si l'application fonctionne dans l'environnement du serveur d'applications. Les liaisons générales sont prises en charge dans l'environnement de client léger, mais les liaisons par défaut ne le sont pas.
La gestion de l'ensemble des règles et des liaisons de l'API client WS-Trust est semblables à celle de l'ensemble de règles et des liaisons d'un client de services Web. Il existe cependant des particularités propres au client WS-Trust. L'une d'entre elles est que le client WS-Trust n'utilise pas d'associations d'ensemble de règles. A la place, les noms de l'ensemble de règles et de la liaison sont indiqués dans l'objet ProviderConfig, comme le montrent les lignes 36 et 37 du modèle de code.
Lorsque le client WS-Trust recherche des liaisons, la façon dont il gère la portée de la recherche diffère de celle du client de services web. Si vous ne définissez pas la propriété wstrustClientBindingScope pour la liaison du client sécurisé, le système commence par rechercher dans l'application une liaison qui lui soit spécifique, avec le nom de liaison défini. S'il trouve une liaison, celle-ci est utilisée pour la demande du client sécurisé. Si le système ne trouve pas de liaison spécifique à l'application, il cherche une liaison générale du même nom. S'il en trouve une, celle-ci est utilisée pour la demande du client sécurisé. Si aucune liaison portant le nom spécifié n'est trouvée, les liaisons par défaut sont utilisées dans un environnement serveur. Les liaisons par défaut ne sont utilisées que dans les environnements serveur. Si la portée de la liaison est définie, seule cette portée est utilisée pour la recherche de liaison.
La ligne 38 du modèle de code, providerConfig.setBindingScope("domain"), indique que l'exemple utilise des liaisons générales. Vous pouvez aussi définir application comme valeur de la portée, pour signifier que le code utilise des liaisons spécifiques à l'application. Dans l'exemple, la liaison générale SamlTCSample et utilisée. Les liaisons spécifiques aux applications et les liaisons générales sont prises en charge dans l'environnement du serveur d'applications et du client léger. Pour plus d'informations sur la configuration des liaisons SamlTCSample lorsque l'application est installée sur le serveur d'applications, lisez la rubrique relative à la configuration des ensembles de règles et des liaisons pour la communication avec le service de jeton de sécurité (STS).
Le code showProviderConfigDefaultValue(providerConfig) à la ligne 34 du modèle montre les paramètres par défaut. Le modèle de code inclut un méthode utilitaire qui imprime le contenu de providerConfig.
La ligne 51 du modèle de code, List<SecurityToken> securityTokens = client.issue(providerConfig, requesterConfig), envoie une demande d'émission WS-Trust. Le second paramètre sur cette ligne spécifie l'objet RequesterConfig, et ce paramètre détermine le contenu de la demande d'émission. Le code de la ligne 41, RequesterConfig requesterConfig = WSSTrustClient.newRequesterConfig(Namespace.WST13), instancie un objet RequesterConfig utilisé pour construire la demande de jeton à l'aide de l'espace de nom de WS-Trust version 1.3. Une fonction de programme utilitaire est illustrée à la ligne 43 : showRequestConfigDefaultValue(requesterConfig). Cette fonction affiche les paramètres par défaut de l'objet RequesterConfig. Le code entre les lignes 45 et 48 initialise RequesterConfig en vue d'une demande de jeton bearer SAML version 1.1. Ce jeton est utilisé pour accéder au noeud final du service en utilisant l'espace de nom SOAP 1.2. Dans l'exemple, noeud final du service est https://user.MyCompany.com:9443/WSSampleSei/EchoService12.
Prise en charge des arguments JVM
Avant d'exécuter le modèle de code, vous devez définir plusieurs arguments JVM (Java™ Virtual Machine). Le modèle implémente l'ensemble de règles Username WSHTTPS Default, qui a deux exigences : 1) un jeton Username est envoyé au service STS, et 2) les messages sont protégés avec SSL (Secure Sockets Layer). Pour configurer l'environnement de façon à répondre à ces exigences, configurez d'abord le fichier ssl.client.props pour définir un fichier de clés. Des instructions détaillées sont disponibles dans la rubrique relative à l'exécution d'un client JAX-WS de services Web non géré.
Pour respecter la seconde exigence de protection du message par SSL, obtenez une copie du certificat STS SSL X.509 et insérez-la dans le fichier de clés certifiées. Cette procédure est détaillée dans la rubrique Utilisation de la commande retrieveSigners dans SSL pour activer la confiance entre serveurs. Sinon, vous pouvez également accepter le certificat du STS lors du premier envoi de demande de jeton au service STS, si la propriété com.ibm.ssl.enableSignerExchangePrompt dans le fichier racine_profil/properties/ssl.client.props contient la valeur true. Pour plus d'informations sur cette option, consultez la rubrique relative à la modification de l'invite d'échange automatique du signataire sur le client.
En outre, le fichier de configuration JAAS du client doit être spécifié, pour que l'environnement d'exécution de celui-ci puisse localiser la configuration JAAS du module de connexion du jeton Username. Définissez ce paramètre en utilisant le code suivant : -Djava.security.auth.login.config="%WAS_HOME%\properties\wsjaas_client.conf. Le fichier jar du client léger, par exemple com.ibm.jaxws.thinclient_9.0.jar, doit également être inclus dans le chemin d'accès aux classes. Pour plus d'informations, consultez la rubrique relative à l'exécution d'un client JAX-WS de services Web non géré.
Exemple d'exécution de code
Une condition préalable à l'exécution du modèle de code est la configuration d'un noeud final de STS externe pour l'émission d'un jeton bearer SAML 1.1 destiné aux services web spécifiés, définis par la propriété RequesterConfiguration.RSTT.APPLIESTO_ADDRESS.
177. <?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
178. <soapenv:Header>
179. <wsa:To xmlns:wsa="http://www.w3.org/2005/08/addressing">https://user.MyCompany.com/Trust/13/UsernameMixed</wsa:To>
180. <wsa:MessageID xmlns:wsa="http://www.w3.org/2005/08/addressing">urn:uuid:4951B6775950CAC92A1252458259166</wsa:MessageID>
181. <wsa:Action xmlns:wsa="http://www.w3.org/2005/08/addressing">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</wsa:Action>
182. </soapenv:Header>
183. <soapenv:Body>
184. <wst:RequestSecurityToken xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
185. <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>
186. <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>
187. <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</wst:KeyType>
188. <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
189. <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
190. <wsa:Address>https://user.MyCompany.com:9443/WSSampleSei/EchoService12</wsa:Address>
191. </wsa:EndpointReference>
192. </wsp:AppliesTo>
193. </wst:RequestSecurityToken>
194. </soapenv:Body>
195. </soapenv:Envelope>
- -DtraceSettingsFile=MyTraceSettings.properties
- -Djava.util.logging.manager=com.ibm.ws.bootstrap.WsLogManager
- -Djava.util.logging.configureByServer=true
Renvoi du jeton SAML
196. <?xml version="1.0" encoding="UTF-8"?>
197. <saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" MajorVersion="1" MinorVersion="1"
198. AssertionID="_f7f65d28-fbb1-4e10-8ddf-f4b6ed0c8277" Issuer="http://MyCompany.com/Trust"
199. IssueInstant="2009-09-09T01:04:41.144Z">
200. <saml:Conditions NotBefore="2009-09-09T01:04:41.141Z" NotOnOrAfter="2009-09-09T11:04:41.141Z">
201. <saml:AudienceRestrictionCondition>
202. <saml:Audience>https://user.MyCompany.com:9443/WSSampleSei/EchoService12</saml:Audience>
203. </saml:AudienceRestrictionCondition>
204. </saml:Conditions>
205. <saml:AuthenticationStatement AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password"
206. AuthenticationInstant="2009-09-09T01:04:41.131Z">
207. <saml:Subject>
208. <saml:SubjectConfirmation>
209. <saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod>
210. </saml:SubjectConfirmation>
211. </saml:Subject>
212. </saml:AuthenticationStatement>
213. <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
214. <ds:SignedInfo>
215. <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
216. <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
217. <ds:Reference URI="#_f7f65d28-fbb1-4e10-8ddf-f4b6ed0c8277">
218. <ds:Transforms>
219. <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
220. <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
221. </ds:Transforms>
222. <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
223. <ds:DigestValue>AQ6e7YQqKgcg/B/ebBj8/DF+uWg=</ds:DigestValue>
224. </ds:Reference>
225. </ds:SignedInfo>
226. <ds:SignatureValue>SuccIOniR . . . . yjTh9iQs=</ds:SignatureValue>
227. <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
228. <X509Data>
229. <X509Certificate>MIIB3zCCAUi . . . . itzymqg3</X509Certificate>
230. </X509Data>
231. </KeyInfo>
232. </ds:Signature>
233. </saml:Assertion>