📜 ⬆️ ⬇️

WSO2: Setting up a proxy to the service with username and password authentication

Task


There is an application that can request data using a specific protocol and there is a service that can send data, but using a different protocol and requires authorization by login and password.
The application does not have the credentials required to access the service. You need to make friends with the application and service.

Decision


Use bus: WSO2 Enterprise Service Bus , on which to configure a proxy service.

We use WSO2 ESB version 4.7.0.

We create policies

Since the target service is protected by the WS-Security standard, we will configure access to it to the fullest extent of the standard.
Policies are created as resources in the local repository, in the interface: Manage - Service Bus - Local Entries - Add Local Entries - Add In-lined XML Entry.
')
Outgoing policy

Here and in the future, arbitrary names can be given, but I will focus on them, because through them all artifacts will be connected in one piece.
Specify name: service-policy
Specify policy:

<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UTOverTransport"> <wsp:ExactlyOne> <wsp:All> <sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> <wsp:Policy> <sp:TransportToken> <wsp:Policy> <sp:HttpsToken RequireClientCertificate="false"/> </wsp:Policy> </sp:TransportToken> <sp:AlgorithmSuite> <wsp:Policy> <sp:Basic128/> </wsp:Policy> </sp:AlgorithmSuite> <sp:Layout> <wsp:Policy> <sp:Lax/> </wsp:Policy> </sp:Layout> <sp:IncludeTimestamp/> </wsp:Policy> </sp:TransportBinding> <sp:SignedSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> <wsp:Policy> <sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient"/> </wsp:Policy> </sp:SignedSupportingTokens> <ramp:RampartConfig xmlns:ramp="http://ws.apache.org/rampart/policy"> <ramp:user>MyUsername</ramp:user> <ramp:passwordCallbackClass>serPasswordCallbackHandler</ramp:passwordCallbackClass> </ramp:RampartConfig> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> 

The main part of this policy is specifying the username of the target service and the class, which in our case will simply substitute the necessary password, the class serPasswordCallBackHandler :

 package ser; import org.apache.ws.security.WSPasswordCallback; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import java.io.IOException; public class PasswordCallBackHandler implements CallbackHandler { private static final String PASSWORD = "MyPassword"; @Override public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback callback : callbacks) { if (callback instanceof WSPasswordCallback) { WSPasswordCallback pc = (WSPasswordCallback) callback; if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN) { if (pc.getPassword().equals(PASSWORD)) return; throw new UnsupportedCallbackException(callback, "Check failed"); } pc.setPassword(PASSWORD); } else { throw new UnsupportedCallbackException(callback, "Unrecognized Callback"); } } } } 


We pack in jar and put in WSO2_HOME / repository / components / lib /.
Restart WSO2.

Inbox Policy

Specify the name: empty-policy .
Specify policy:

 <wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UTOverTransport"> <wsp:ExactlyOne> <wsp:All> <sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> <wsp:Policy/> </sp:TransportBinding> <sp:SignedSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> <wsp:Policy/> </sp:SignedSupportingTokens> </wsp:All> </wsp:ExactlyOne> </wsp:Policy> 


Create Address Endpoint

Specify the name: my-endpoint .
Specify the address of the service.
In the additional options, we mark the use of WS-Security and specify the policies for outgoing and incoming messages created earlier.
Endpoint Source:

 <endpoint name="my-endpoint"> <address uri="http://service/soap/"> <enableSec inboundPolicy="empty-policy" outboundPolicy="service-policy"/> </address> </endpoint> 


Creating an XSLT Message Transformation

XSLT transformations are created in the same place as policies: Manage - Service Bus - Local Entries - Add Local Entries - Add In-lined XML Entry.

Convert incoming message

We need to bring the request of our application to the form that the service understands:
Specify name: in-xslt
Specify the conversion:

 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <!--  --> </xsl:template> </xsl:stylesheet> 


Conversion to outgoing message

We need to bring the response of the service to a form that the application can understand. The step is identical to the step for the incoming message, only the name: out-xslt .

We create Proxy Service

In UI: Manage - Service - Add - Proxy Service - Custom Proxy
Specify the name: external-service . By this name, your service will be available on the bus, for example: localhost : 8280 / services / external-service.
In our case, remove the mark from the https protocol.
Next, switch to source code (switch to source view) and bring the contents to something like the following:

 <?xml version="1.0" encoding="UTF-8"?> <proxy xmlns="http://ws.apache.org/ns/synapse" name="external-service" transports="http" statistics="disable" trace="disable" startOnLoad="true"> <target faultSequence="fault" endpoint="my-endpoint"> <inSequence> <xslt key="in-xslt"/> <send> <endpoint key="my-endpoint"/> </send> </inSequence> <outSequence> <xslt key="out-xslt"/> <send/> </outSequence> </target> <description/> </proxy> 


There are two sequences for the incoming and outgoing message, both start with the appropriate conversion and then send the message in the right direction.

Done!

Additionally



To keep track of what our proxy messages turn into, used fiddler

Because I did not find how to set up and where to look for the final messages.
More about fiddler :


The proxy forwards the Action header that came from the application, and the service swears?

Add mediators to the proxy service sequence, remove the Action header:

 <inSequence> <xslt key="in-xslt"/> <header name="Action" value=""/> <property name="SOAPAction" value="" scope="transport"/> <send> <endpoint key="my-endpoint"/> </send> </inSequence> 


Proxy returns data in incorrect encoding?

Specify the encoding in which we return messages:

 <outSequence> <xslt key="out-xslt"/> <property name="messageType" value="text/xml;charset=windows-1251" scope="axis2" type="STRING"/> <send/> </outSequence> 


The answer to the service consists of several parts?

For example:

 <soapenv:Envelope> <soapenv:Body> <part1/> <part2/> </soapenv:Body> </soapenv:Envelope> 


WSO2 by default considers that the answer should consist of one part, therefore, transfer what is on the way to the transformation: s11: Body / child :: * [position () = 1] | s12: Body / child :: * [position () = 1], as a result we can only transform the first part, to fix this change the call to the converter in the service:

 <outSequence> <xslt xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" key="stav-out-xslt" source="SOAP-ENV:Body"/> <send/> </outSequence> 


... and take this into account in the converter:

 <xsl:stylesheet> <xsl:template match="."> <xsl:apply-templates select="/part1"/> <xsl:apply-templates select="/part2"/> </xsl:template> <!--   part1   --> <xsl:template match="part1"/> <xsl:template match="part2"> <forbody> <!--     ,  ,         --> </forbody> </xsl:template> </xsl:stylesheet> 


Unsolved Issues



I had to configure two security policies

There is a feeling that one can do without one, but there was an error with a single policy, stating that the response of the service does not contain security headers. And he really does not contain them. To get around this, I had to write an empty policy for an answer.

No built-in solution for storing third-party service credentials found.

Awkward solution with a class for storing a password is an obvious crutch that needs to be replaced with a suitable solution, either built-in or bicycle.

Total


Source: https://habr.com/ru/post/212267/


All Articles