How to use ws-security info per sending soap message in CXF? - java

I like to be able to dynamically change/set the ws-security info per sending soap message in cxf. How can this best be done.
Details: I want to change settings like keystore name, keystore alias, password, host name, etc.. during runtime, preferable per sending message.
Currently I am using: jaxws client with WSS4JOutInterceptor and WSS4JInInterceptor interceptor for signing. And I am using http conduit with tls client parameters for SSL/TLS communications.
Both the jaxws client and http conduit are configured in spring and have their configuration like keystore name, alias and password set in the Spring config.
I see options:
1) I change these settings during runtime through a global property.
2) I change these settings per sending message (preferable).
2) Is preferable but most difficult I think.
How should I do this?
I was thinking about:
a) jaxws client: make my own in- and out interceptor that intercepts a message and use the correct (cached) WSS4JOutInterceptor interceptor depending on the security settings that that message requires. If the WSS4JOutInterceptor interceptor doesn't exists in cache, it's created (I probably have max 5 WSS4JOutInterceptor instances cached).
However, how can I determine which settings are required in my interceptor as that is known in an other part of the app when talking to the #Webservice proxy to create and send the soap request/message...
Maybe I could be able to add some kind of securityInfo object to the soap message through the JaxWsClientProxy, but how ?...
Or maybe I can set the interceptors when sending/creating the soap message when this security info is still know. This will then be a light weight interceptor containing the required settings, linking to the cached interceptors, that is selected depending on the settings...
b) http conduit: using a ConduitSelector (never used it but will find out), such that I am able to select the correct http conduit, but I have the same problem as in (a): "How to determine which settings I should use" as they are known when creating/sending a soap message and the interceptors are set later...
Probably I have to set an conduit selector per sending message..
At the end the above become a big story ;), but I hope it's clear you have can give some advice?

I have found the org.apache.cxf packages to be very helpful in keeping me from having to write custom classes and have been able to stick to the basic interface.
For your instance, the approach might look like:
use a JAX-WS or wsimport client
use the cxf WSS4JOutInterceptor with dynamic properties
use a dynamic CallbackHandler class for access to the keystore
A typical (wsimport'd) client public API might look like:
public class SomeServiceClient
{
public SomeService getSomeService( URL url )
{
SomeService_Service svc = new SomeService_Service();
SomeService someService = svc.getSomeServicePort();
Client client = ClientProxy.getClient( someService );
Endpoint cxfEP = client.getEndpoint();
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put( WSHandlerConstants.ACTION, "Signature" );
outProps.put( WSHandlerConstants.USER, "foo" );
outProps.put( WSHandlerConstants.PW_CALLBACK_CLASS, SomeClientCallbackHandler.class.getName() );
outProps.put( WSHandlerConstants.SIG_PROP_FILE, "client-sign.properties" );
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor( outProps );
cxfEP.getOutInterceptors().add( wssOut );
return someService;
}
}
You could provide some logic to determine which WSHandlerConstants.USER value to pass, which PW_CALLBACK_CLASS to use, and which SIG_PROP_FILE to use.
The properties file could look like the following. You could simply have multiple files to choose from, or you could just dynamically add these properties in the class:
# properties for accessing the java keystore using Merlin
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=bar
org.apache.ws.security.crypto.merlin.keystore.alias=foo
org.apache.ws.security.crypto.merlin.keystore.file=foobar.keystore
Finally, your CallbackHandler will need to provide the password for the alias of the cert you identified in the keystore. This could also have some dynamic logic in it.
public class SomeClientCallbackHandler implements CallbackHandler
{
#Override
public void handle( Callback[] callbacks ) throws IOException, UnsupportedCallbackException
{
for( Callback thisCallback : callbacks )
{
WSPasswordCallback pwcb = (WSPasswordCallback)thisCallback;
String user = pwcb.getIdentifier();
int usage = pwcb.getUsage();
if( usage == WSPasswordCallback.SIGNATURE )
{
if( "foo".equals( user ) ) pwcb.setPassword( "bar" );
}
}
}
}
In the end, there is not much more work to make this all 'dynamic' to handle multiple keystores versus the work needed to just do it once. It might be preferable over coding your own 'in' or 'out' interceptors.

The accepted answers to this question appear to be (from http://www.mail-archive.com/users#cxf.apache.org/msg29804.html):
Hi Ed,
As I already wrote, you don't even need the interceptor to change the
properties. You can do it in your client by sending message:
AddNumbers port = (AddNumbers)service.getPort(portName,
AddNumbers.class);
((BindingProvider)port).getRequestContext().put(SecurityConstants.ENCRYPT_PROPERTIES,
).
Although, If you would like to do it in interceptor, you can pass
necessary information with message properties using the same technic.
The most standard and recommended way to control security in CXF is
using WS-Policy. You also can apply it dynamically at the runtime. If
this way is interesting for you, I can provide further information
how to set WS-Policy dynamically.
Cheers, Andrei.
(from http://www.mail-archive.com/users#cxf.apache.org/msg29809.html):
If you do:
((BindingProvider)port).getRequestContext().put("thread.local.request.context",
"true");
than future calls to getRequestContext() will use a thread local
request context. That allows the request context to be threadsafe.
(Note: the response context is always thread local in CXF).
Details in
http://cxf.apache.org/faq.html#FAQ-AreJAXWSclientproxiesthreadsafe%3F
Cheers, Andrei.

Related

How to extend expiration time of SOAP header message timestamp

Is there a way to prolong time to live(expiration time) of a SOAP message header timestamp to prevent
"WSSecurityException: The message has expired" exception. The default is set to 300 seconds(5 minutes). We would like to extend it to 10 minutes.
We have a java ee(jdk 1.8.0_77) service that uses SOAP messages to communicate with android client.
It runs on Jboss Wildfly 10.0.0. server, and uses Apache CXF(version 3.1.4) WS security to handle messages. wss4j security policy is version 2.1.4
We have wsdl file, Custom WS-Security Endpoint (server-endpoint-config.xml file), and our Schema.xsd file. I don't know what parts of those files are relevant, so I'll provide them if someone needs more info.
I've tried using a custom interceptor that extends WSS4JInInterceptor, but that part of code never gets executed.
#WebService
(
portName = "HelloPort",
serviceName = "BampayService",
wsdlLocation = "WEB-INF/BampayService.wsdl",
targetNamespace = "http://bampay.bamcard.ba/",
endpointInterface = "ba.bamcard.bampay.Hello"
)
#EndpointConfig
(
configFile = "WEB-INF/server-endpoint-config.xml",
configName = "Custom WS-Security Endpoint"
)
#InInterceptors(// I added this part
interceptors = {"ba.bamcard.helpers.MyInterceptor"}
)
and the code of MyInterceptor
public class MyInterceptor extends WSS4JInInterceptor {
#Override
public int decodeTimeToLive(RequestData reqData, boolean timestamp) {
// return super.decodeTimeToLive(reqData, timestamp);
return 600;
}
}
If needed, I will gladly provide any additional information.
If anyone can help me, I would be very grateful.
Add this property to your server-side security settings to the value you want.
ws-security.timestamp.timeToLive
If you think you are seeing clock-skew between the client(s) and the server as part of the reason for the errors, you'll also want to account for clients that send messages with timestamps in the future.
ws-security.timestamp.futureTimeToLive
ref: https://cxf.apache.org/docs/ws-securitypolicy.html

apache cxf LoggingFeature mask senstive information

I'm using Logging feature to log in/out message to my cxf rest server on Spring boot. Similarly using the same to log outward Rest API connections initiated by cxf WebClient.
I came across few parameters which I do not want to be logged in to the log file. either completely removing them or masking them is sufficient for my case.
I found on the internet that previous (now depreciated) LoginIntercepter had transform operation to modify the log entry. I was not able to find a solution to mask/truncate the log entries wirg LoggingFeature.
any help would be appreciated
Current configuration of the server's logging feature is like below.
factory.setProviders(providers);
LoggingFeature loggingFeature = new LoggingFeature();
loggingFeature.setPrettyLogging(true);
loggingFeature.setLogBinary(false);
loggingFeature.setLogMultipart(false);
factory.getFeatures().add(loggingFeature);
Server server = factory.create();
web client configuration is as below
LoggingFeature loggingFeature = new LoggingFeature();
loggingFeature.setPrettyLogging(true);
WebClient client = WebClient.create(url, Collections.singletonList(new JacksonJsonProvider()),
Arrays.asList(loggingFeature), null);
To change the log message, you can ...
Write a custom LogSender and set it on the LoggingFeature to do custom
logging. All meta data can be access[ed] from the class LogEvent.
(Source: http://cxf.apache.org/docs/message-logging.html)
With a sender like:
// ...
import org.apache.cxf.ext.logging.event.LogEvent;
class MyLogEventSender implements org.apache.cxf.ext.logging.event.LogEventSender {
#Override
public void send(LogEvent event) {
event.setPayload(maskSensibleParameters(event.getPayload()));
}
private String maskSensibleParameters(String pIn) {
// here goes the tricky part
// ... but no details on this in your question
// ... here you can stick to "old" LogInterceptor examples
// ... and also to PrettyLoggingFilter.
}
}
A code example is given by (the default) PrettyLoggingFilter.
To mask (hide) it (completely) is easier and guaranteed more performant, it depends on the used "logging framework" (java.util, log4j or slf4j) and accomplished with an according "logger configuration". (see here)

Remove filter from JAX RS client

I needed to add an authorisation header to all outgoing requests from the client, and I added a ClientRequestFilter as instructed in this answer.
Now I want to change the properties of the filter I created. I tried to re-register a new copy of the filter but that didn't work. I tried to look through the available methods of the Client object but didn't find anything like de-register or remove.
Sample Code:
Client client = ClientBuilder.newClient().register(new ClientObjectMapperProvider());
AuthTokenFilter authTokenFilter = new AuthTokenFilter(authTokenService, authConfig);
client.register(authTokenFilter);
//make some requests
//In another method in another class
AuthTokenFilter newFilter = new AuthTokenFilter(authTokenService, newConfig);
client.register(newFilter);
//make more requests
When I send requests after registering the new filter, I want the auth header to be different according to the new config, but that's not happening.
On further investigation it seems that the client object will not register an object if another of the same type is already registered. Still haven't found a way to de-register though.
Creating Client instances is quite heavyweight so you don't want to do that very often, hence it makes sense that you are caching and reusing the client instance.
If you simply wish to have a separate authentication filter per targeted endpoint then registration doesn't have to be done on the client instance. Before you can actually talk to anything you need to create a WebTarget (which represents a concrete endpoint to which you are to communicate) Once you've created your WebTarget you can register your filter with that.
Configuring webtarget's can also be fairly heavy weight (see here)
but it does mean that you can spawn (and cache) different authentication filters per targeted host.
If you are wanting to dynamically reconfigure your client/webtarget based on some edited configuration (e.g. properties file/configuration REST endpoint) then you
can simply have a cache of WebTarget/Client instances which you regenerate from scratch when the configuration changes. (You need to remember to call client.close() before discarding any old client instance)

How do I autmatically set a JAX-WS HTTP header that varies on each request

I want to set a dynamically generated HTTP header on each SOAP JAX-WS request.
If I wanted to set the same HTTP header on each JAX-WS request I could use the technique here, i.e.
public class MyApplicationClass {
// Inject an instance of the service's port-type.
#WebServiceRef(EchoService.class)
private EchoPortType port;
// This method will invoke the web service operation and send transport headers on the request.
public void invokeService() {
// Set up the Map that will contain the request headers.
Map<String, Object> requestHeaders = new HashMap<String, Object>();
requestHeaders.put(“MyHeader1”, “This is a string value”);
requestHeaders.put(“MyHeader2”, new Integer(33));
requestHeaders.put(“MyHeader3”, new Boolean(true));
// Set the Map as a property on the RequestContext.
BindingProvider bp = (BindingProvider) port;
bp.getRequestContext().put(com.ibm.websphere.webservices.Constants.REQUEST_TRANSPORT_PROPERTIES, requestHeaders);
// Invoke the web services operation.
String result = port.echoString(“Hello, world!”);
}
}
However, here I want to use a different HTTP header for each request. Essentially I want to include a X-RequestId header or similar with a random value, so the receiving server can distinguish between requests duplicated on a timeout either by the Java client or (worse) an inline HTTP proxy.
Moreover, it JAX-WS retries the same call, I don't want it to regenerate the header (obviously).
Note that my application is already covered in the equivalent of port.echoString (lots of calls to the web service). I can't manually change the header in front of each such call because:
they share the same binding provider, and this would not be thread-safe (i.e. user A could change the header, user B could change the header, then user A could call, then user B could call, and the same header be passed)
this would require modification all over the code.
What I want to do is add something to the class that serialises each request, to add the header at serialisation time.
Questions that are related but are not duplicates:
java web service client, adding http headers - does not use JAX-WS binding, i.e. each call has to be made manually
How to add header to SOAP request? - SOAP headers not HTTP headers
As for the unique value aspect, you can use the JDK's UUID class to create a GUID:
requestHeaders.put("X-RequestId", java.util.UUID.randomUUID().toString());
As for the clarified thread safety concern, based on the JAX-WS specification (JSR-224) section 9.3 I'd suggest using a JAX-WS client handler to do this as the handler spec identifies a thread safe mechanism: MessageContext:
9.3.3 Handler Implementation Considerations
Handler instances may be pooled by a JAX-WS runtime system. All
instances of a specific handler are considered equivalent by a JAX-WS
runtime system and any instance may be chosen to handle a particular
message. Different handler instances may be used to handle each
message of an MEP. Different threads may be used for each handler in a
handler chain, for each message in an MEP or any combination of the
two. Handlers should not rely on thread local state to share
information. Handlers should instead use the message context, see section 9.4.
You can write one central handler class and attach it to the BindingProvider's handler chain to avoid changing all the places you invoke the service operation across the application. You can add a handler to the handler chain programmatically or via the #HandlerChain annotation companion to #WebServiceRef
This post describes using the handler framework's MessageContext to set outbound http headers like you want. However in your case you want to set the X-RequestId with the UUID value discussed above.

Securing REST services in Jersey

I am very much new to web services. I have exposed some REST services using Jersey 2 in integration with Spring. Now I need to secure those rest services using authentication with username/password. I am told not to use Spring Security.
I have no idea of how to do this. I did search on the net but various links show various implementation and I am unable to decide how to proceed with it.
A common way for authenticating with username and password is to use Basic Authentication. Basically the client needs to send a request header Authorization, with the the header value as Basic Base64Encoded(username:password). So is my username is peeskillet and my password is pass, I, as a client, should set the header as
Authorization: Basic cGVlc2tpbGxldDpwYXNz
In a servlet environment, the container should have support for Basic authentication. You would configure this support on the web.xml. You can see an example in 48.2 Securing Web Applications of the Java EE tutorial. You will also notice in an example
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
That is for SSL support. This is recommended for Basic Authentication.
If you don't want to deal with the hassle of working with security domains and login modules, realm, and such, that would be required to customize the servlet support, or if you're just not in a servlet environment, implementing Basic Auth in a ContainerRequestFilter is really not too difficult.
You can see a complete example of how this could be done at jersey/examples/https-clientserver-grizzly. You should focus on the SecurityFilter
The basic flow in the filter goes something like this
Get the Authorization header. If it doesn't exist, throw an AuthenticationException. In which case the AuthenticationExceptionMapper will send out the header "WWW-Authenticate", "Basic realm=\"" + e.getRealm() + "\", which is part of the Basic Auth protocol
Once we have the header, we parse it just to get the Base64 encoded username:password. Then we decode it, then split it, then separate the user name and password. If any of this process fails, again throw the WebApplicationException that maps to a 400 Bad Request.
Check the username and password. The example source code just checks if the username is user and the password is password, but you will want to use some service in the filter to verify this information. If either of these fail, throw an AuthenticationException
If all goes well, a User is created from the authenticate method, and is injected into an Authorizer (which is a SecurityContext). In JAX-RS, the SecurityContext is normally used for authorization`.
For the authorization, if you want to secure certain areas for certain resources, you can use the #RolesAllowed annotation for your classes or methods. Jersey has support for this annotation, by registering the RolesAllowedDynamicFeature.
What happens under the hood is that the SecurityContext will be obtained from the request. With the example I linked to, you can see the Authorizer, it has an overridden method isUserInRole. This method will be called to check against the value(s) in #RolesAllowed({"ADMIN"}). So when you create the SecurityContext, you should make sure to include on the overridden method, the roles of the user.
For testing, you can simply use a browser. If everything is set up correctly, when you try and access the resource, you should see (in Firefox) a dialog as seen in this post. If you use cURL, you could do
C:/>curl -v -u username:password http://localhost:8080/blah/resource
This will send out a Basic Authenticated request. Because of the -v switch, you should see all the headers involved. If you just want to test with the client API, you can see here how to set it up. In any of the three cases mentioned, the Base64 encoding will be done for you, so you don't have to worry about it.
As for the SSL, you should look into the documentation of your container for information about how to set it up.
So this is really a matter what you would like to achieve. My case was to get this thing running with mobile and a One-Page-App JavaScript.
Basically all you need to do is generate some kind of header that value that will be needed in every consecutive request you client will make.
So you do a endpoint in which you wait for a post with user/password:
#Path("/login")
public class AuthenticationResource {
#POST
#Consumes("application/json")
public Response authenticate(Credentials credential) {
boolean canBeLoggedIn = (...check in your DB or anywher you need to)
if (canBeLoggedIn) {
UUID uuid = UUID.randomUUID();
Token token = new Token();
token.setToken(uuid.toString());
//save your token with associated with user
(...)
return Response.ok(token).type(MediaType.APPLICATION_JSON_TYPE).build();
} else {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
}
}
Now you need to secure resource with need for that token:
#Path("/payment")
#AuthorizedWithToken
public class Payments {
#GET
#Produces("application/json")
public Response sync() {
(...)
}
}
Notice the #AuthorizedWithToken annotation. This annotaation you can create on your own using special meta annotation #NameBinding
#NameBinding
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface AuthorizedWithToken {}
And now for the filter that implements checking of the header:
#AuthorizedWithToken
#Provider
public class XAuthTokenFilter implements ContainerRequestFilter {
private static String X_Auth_Token = "X-Auth-Token";
#Override
public void filter(ContainerRequestContext crc) throws IOException {
String headerValue = crc.getHeaderString(X_Auth_Token);
if (headerValue == null) {
crc.abortWith(Response.status(Response.Status.FORBIDDEN).entity("Missing " + X_Auth_Token + " value").build());
return;
}
if(! TOKEN_FOUND_IN_DB) {
crc.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("Wrong " + X_Auth_Token + " value").build());
return;
}
}
}
You can create any number of your own annotations checking for various things in the http request and mix them. However you need to pay attention to Priorities but that actually easy thing to find. This method needs using https but that is obvious.
Security comes in two main flavours :
Container Based
application based
the standard way to secure spring applications is to use Spring Security (formerly Acegi).
It would be interesting to know why you're not being allowed to use that.
You could use container based security, but I'm guessing that your use of spring precludes that option too.
Since the choice of Spring is usually to obviate the need for the use of a full J2EE container (Edit : though as pointed out below by others, most ordinary servlet containers do allow you to implement various container based security methods)
This really only leaves you with one option which is to roll your own security.
Your use of Jersey suggests that this might be a REST application.
In which case you really ought to stick with standard HTTP Authentication methods that
comes in the following flavours in reverse order of strength :
BASIC
Digest
Form
Certificate
REST applications are usually supposed to be 'stateless', which essentially rules out form based authentication (because you'd require the use of Session)
leaving you with BASIC, Digest and Certificate.
Your next question is, who am I authenticating. If you can expect to know the username AND the password of the user based on what URL they requested (say if it's one set of credentials for all users) then Digest is the best bet since the password is never sent, only a hash.
If you cannot know the Password (because you ask a third party system to validate it etc.) then you are stuck with BASIC.
But you can enhance the security of BASIC by using SSL, or better yet, combining BASIC with client certificate authentication.
In fact BASIC authentication over HTTPS is the standard technique for securing most REST applications.
You can easily implement a Servlet Filter that looks for the Authentication Header and validates the credentials yourself.
There are many examples of such filters, it's a single self contained class file.
If no credentials are found the filter returns 401 passing a prompt for basic auth in the response headers.
If the credentials are invalid you return 403.
App security is almost an entire career in itself, but I hope this helps.
As the former posts say, you could go with different options, with a varying overhead for implementation. From a practical view, if you're going to start with this and are looking for a comfortable way for a simple implementation, I'd recommend container-based option using BASIC authentication.
If you use tomcat, you can setup a realm, which is relatively simple to implement. You could use JDBCRealm, which gets you a user and password from specified columns in your database, and configure it via server.xml and web.xml.
This will prompt you for credentials automatically, everytime you are trying to access your application. You don't have any application-side implementation to do for that.
What I can tell you now is that you already did most of the 'dirty' job integrating Jersey with Spring. I recommend to you to go an Application-based solution, is it does not tie you to a particular container. Spring Security can be intimidating at first, but then when you tame the beast, you see it was actually a friendly puppy.
The fact is that Spring Security is hugely customizable, just by implementing their interfaces. And there is a lot of documentation and support. Plus, you already have a Spring based application.
As all you seek is guidance, I can provide you with some tutorials. You can take advantage from this blog.
http://www.baeldung.com/rest-with-spring-series/
http://www.baeldung.com/2011/10/31/securing-a-restful-web-service-with-spring-security-3-1-part-3/

Categories

Resources