webservice client using cxf (JaxWsProxyFactoryBean) to test digest authentication - spring-security

How do I setup digest auth type to webservice client using cxf (JaxWsProxyFactoryBean) ? As of now my client by default assumes Basic auth type when I set the username password in the factory object. And the request message header have "Authentication [Basic: ......]" in it. I want to setup a cxf client for digest auth. could you please assist ?
(WS is implemented in Java using CXF and Spring 3)
I have configured spring security for Digest auth as follows...
<security:http entry-point-ref="digestEntryPoint">
<security:intercept-url pattern="/**" requires-channel="any" access="ROLE_WS_USER" />
<security:custom-filter ref="digestAuthenticationFilter" position="BASIC_AUTH_FILTER"/>
</security:http>
<bean id="digestAuthenticationFilter" class="org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
<property name="userDetailsService" ref="userService"/>
<property name="authenticationEntryPoint" ref="digestEntryPoint"/>
</bean>
<bean id="digestEntryPoint" class="org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
<property name="realmName" value="My Webservice"/>
<property name="key" value="acegi"/>
<property name="nonceValiditySeconds" value="10" />
</bean>
<bean id="userService" class="com.wallstreetsystems.ws.config.SpringSecurityUserDetailsService">
</bean>
<security:authentication-manager>
<security:authentication-provider user-service-ref="userService"/>
</security:authentication-manager>
And the java client is...
public static void main(String args[]) throws Exception {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(OrderNUmber.class);
factory.setAddress(ht..://localh..:9000/ws/OderNumberService);
factory.setUsername("bob");
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
outProps.put(WSHandlerConstants.USER,"bob");
outProps.put(WSHandlerConstants.PASSWORD_TYPE,WSConstants.PW_DIGEST);
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordCallback.class.getName());
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
factory.getOutInterceptors().add(wssOut);
String orderNo = orderNumber.getNextOrderNumber();
System.out.println(orderNo);
}
I am deploying and running test on embedded Jetty. Error log is as follows....
ID: 1
Address: ht..://localh...:9000/ws/OrderNumberService
Encoding: UTF-8
Content-Type: text/xml
Headers: {Accept=[*/*], SOAPAction=["getNextOrderNumber"]}
Payload: <soap:Envelope xmlns:soap="ht..://schemas.xmlsoap.org/soap/envelope/"><soap:Header><wsse:Security xmlns:wsse="ht..://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="ht..://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1"><wsse:UsernameToken wsu:Id="UsernameToken-1"><wsse:Username>bob</wsse:Username><wsse:Password Type="ht..://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">bobspassword</wsse:Password></wsse:UsernameToken></wsse:Security></soap:Header><soap:Body/></soap:Envelope>
--------------------------------------
2013-09-23 14:07:36,887 [294071597#qtp-1153274506-2] INFO org.apache.cxf.services.OrderNumberService.OrderNumberPort.OrderNumber - Inbound Message
----------------------------
ID: 2
Address: ht..://localhost:9000/ws/OrderNumberService
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml; charset=UTF-8
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], Content-Length=[616], content-type=[text/xml; charset=UTF-8], Host=[localhost:9000], Pragma=[no-cache], SOAPAction=["getNextOrderNumber"], User-Agent=[Apache CXF 2.6.2]}
Payload: <soap:Envelope xmlns:soap="ht..://schemas.xmlsoap.org/soap/envelope/"><soap:Header><wsse:Security xmlns:wsse="ht..://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="ht..://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1"><wsse:UsernameToken wsu:Id="UsernameToken-1"><wsse:Username>bob</wsse:Username><wsse:Password Type="ht..://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">bobspassword</wsse:Password></wsse:UsernameToken></wsse:Security></soap:Header><soap:Body/></soap:Envelope>
--------------------------------------
2013-09-23 14:07:36,911 [294071597#qtp-1153274506-2] WARN org.apache.cxf.phase.PhaseInterceptorChain - Interceptor for {ht..://www.xyz.com}OrderNumberService#{ht..://www.xyz.com}getNextOrderNumber has thrown exception, unwinding now
org.apache.cxf.binding.soap.SoapFault: MustUnderstand headers: [{ht..://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security] are not understood.
at org.apache.cxf.binding.soap.interceptor.MustUnderstandInterceptor.checkUltimateReceiverHeaders(MustUnderstandInterceptor.java:150)
at org.apache.cxf.binding.soap.interceptor.MustUnderstandInterceptor.handleMessage(MustUnderstandInterceptor.java:96)
at org.apache.cxf.binding.soap.interceptor.MustUnderstandInterceptor.handleMessage(MustUnderstandInterceptor.java:49)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:262)
at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:211)
at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:213)
at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:193)
at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:130)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:221)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:141)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:197)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1148)
at com.myproject.ws.config.WSSWebSecurityFilterChain.doFilter(WSSWebSecurityFilterChain.java:64)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1139)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:378)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:324)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:535)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:880)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:747)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:520)
2013-09-23 14:07:36,940 [main] INFO org.apache.cxf.services.OrderNumberService.OrderNumberPort.OrderNumber - Inbound Message
----------------------------
ID: 1
Response-Code: 500
Encoding: UTF-8
Content-Type: text/xml; charset=utf-8
Headers: {Content-Length=[340], content-type=[text/xml; charset=utf-8], Server=[Jetty(6.1.15)]}
Payload: <soap:Envelope xmlns:soap="ht..://schemas.xmlsoap.org/soap/envelope/"><soap:Body><soap:Fault><faultcode>soap:MustUnderstand</faultcode><faultstring>MustUnderstand headers: [{ht..://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security] are not understood.</faultstring></soap:Fault></soap:Body></soap:Envelope>
--------------------------------------
2013-09-23 14:07:37,021 [main] INFO /ws - Closing Spring root WebApplicationContext
Problem : As you can see in the request message header there is no "Authentication [Digest:...." and the request is treated as normal one without auth. But when request reaches service provider which is configured to intercept the Digest request throws the error.
I wanted to set the Authentication type to Digest in the client, so that request would be treated as Digest request.

This is how I fixed it:
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setAddress("localhost:9000/OrderNumberService");
OrderNumberService orderNumberClient = factory.create(OrderNumberService.class);
Client client = ClientProxy.getClient(orderNumberClient);
HTTPConduit http = (HTTPConduit) client.getConduit();
AuthorizationPolicy authPolicy = new AuthorizationPolicy();
authPolicy.setAuthorizationType("Digest");
authPolicy.setUserName("foo");
authPolicy.setPassword("bar");
http.setAuthorization(authPolicy);

Related

Apache Camel CXF Unexpected Element

I have an Apache Camel project that uses CXF to send a SOAP payload . The project has been working flawlessly for over a year.
Recently, I was given a new WSDL to implement for this service ...nothing in the WSDL is suppose to change the payload
I am sending ( same classes and value objects, etc. )...the changes in the WSDL were suppose to be totally unrelated to
the payload I am sending.
I used wsdl2java, in my Maven POM, to generate the java sources from the WSDL ( as I did before ) and all compiles just fine & service deploys fine ( JBoss Fuse ).
When I send the payload however, the service complains that there is an "unexpected element" ...this is
strange since I did not change the payload and the Soap "Operation Name" I am calling is the same as with the old
WSDL ...the new WSDL has a different "serviceClass" name, however, the method I need to invoke is the same.
In the log output, I see the soapAction is not set but I am not sure this should matter ...once again, the
code ran fine with the previous WSDL which essentially has the same serviceClass elements.
As can be seen in the code fragment, I am attempting to use the createEDIInvoices operation from the WSDL ...that
operation expects a createEDIInvoices object ( which I am definately passing ) ...the log though shows
that the SOAP server was expecting createEDIInvoicesResponse :
Unexpected element {http://xmlns.inspyrus.com/ExternalInspyrusService}createEDIInvoices found.
Expected {http://xmlns.inspyrus.com/ExternalInspyrusService}createEDIInvoicesResponse.
Anyhow, opinions regarding the issue are welcome... I'm kinda wondering if I am not doing enough to specify the proper SOAP operation to execute
Here is the relevant code fragment from my Camel route ...note that the code blows on the to("cxf:...") call ...the bean is never called
from("direct:inspyrus-processing")
// specify SOAP operation as defined in WSDL
.setHeader(CxfConstants.OPERATION_NAME,
constant("createEDIInvoices"))
.setHeader(Inspyrus_Auth_Header, simple("{{inspyrus.auth.header}}"))
.to("cxf:http://fergusonsbx.invoice-automation.com/ExternalInspyrusService/ExternalInspyrusService" +
"?serviceClass=com.inspyrus.generated.invoiceprocessingservice.ExternalInspyrusService" +
"&wsdlURL=wsdl/Inspyrus_030119.wsdl" + "&cxfEndpointConfigurer=#cxfConfigurer" + "&loggingFeatureEnabled=true")
.to("bean:parse-inspyrusresponse")
.removeHeader(Inspyrus_Auth_Header)
.log(LoggingLevel.DEBUG, Constants.APP_ID + ": Inspyrus Processing Complete");
The CXFConfigurer code is below:
ManagedBean
#Named("cxfConfigurer")
public class CxfConfigurer implements CxfEndpointConfigurer {
private static final long CXF_CONNECTION_TIMEOUT = 180000;
private static final long CXF_RECEIVE_TIMEOUT = 360000;
#Override
public void configure(final AbstractWSDLBasedEndpointFactory factoryBean) {
}
#Override
public void configureClient(final Client client) {
final HTTPConduit http = (HTTPConduit) client.getConduit();
final HTTPClientPolicy policy = http.getClient();
policy.setAutoRedirect(true);
policy.setConnection(ConnectionType.KEEP_ALIVE);
policy.setConnectionTimeout(CXF_CONNECTION_TIMEOUT); // default is 30K
policy.setReceiveTimeout(CXF_RECEIVE_TIMEOUT); // default is 60K
// Don't be too strict with CN checking
final TLSClientParameters tlsCP = new TLSClientParameters();
tlsCP.setDisableCNCheck(true);
http.setTlsClientParameters(tlsCP);
}
#Override
public void configureServer(final Server server) {
}
The WSDL was generated from http://fergusonsbx.invoice-automation.com/ExternalInspyrusService/ExternalInspyrusService?wsdl ...it is below
<?xml version='1.0' encoding='UTF-8'?><!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is Oracle JAX-WS 2.1.5. --><!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is Oracle JAX-WS 2.1.5. --><definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://xmlns.inspyrus.com/ExternalInspyrusService" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://xmlns.inspyrus.com/ExternalInspyrusService" name="ExternalInspyrusService">
<types>
<xsd:schema>
<xsd:import namespace="http://xmlns.inspyrus.com/ExternalInspyrusService" schemaLocation="ExternalInspyrusService_1.xsd"/>
</xsd:schema>
</types>
<message name="createEDIInvoices">
<part name="parameters" element="tns:createEDIInvoices"/>
</message>
<message name="createEDIInvoicesResponse">
<part name="parameters" element="tns:createEDIInvoicesResponse"/>
</message>
<portType name="ExternalInspyrusService">
<operation name="createEDIInvoices">
<input message="tns:createEDIInvoices"/>
<output message="tns:createEDIInvoicesResponse"/>
</operation>
</portType>
<binding name="ExternalInspyrusServicePortBinding" type="tns:ExternalInspyrusService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="createEDIInvoices">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="ExternalInspyrusService">
<port name="ExternalInspyrusServicePort" binding="tns:ExternalInspyrusServicePortBinding">
<soap:address location="http://fergusonsbx.invoice-automation.com:80/ExternalInspyrusService/ExternalInspyrusService"/>
</port>
</service>
</definitions>
Relevant Log Fragments
ID: 1
Address: http://fergusonsbx.invoice-automation.com/ExternalInspyrusService/ExternalInspyrusService
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml
Headers: {Accept=[*/*], accountId=[SANAN], Authorization=[n5zMltp5l2z2L+2tDC9l], breadcrumbId=[ID-fuse-localdomain-43307-1551479341458-13-1], Connection=[Keep-Alive], DESTINATION_SELL_LOCATION=[454], EDI_810_PONUMBER=[L454-11593], ISFREIGHT=[false], MAIN_BRANCH_NUMBER=[0061], PO_TYPE=[DIRECT], PROCESSED=[true], **SOAPAction=[""]**, SOURCE_PARTY_ID=[22449], UNPROCESSED_VENDORID=[SANAN*10770~1~1], UNPROCESSED_VENDORNUMBER=[10770], WAREHOUSE_NUMBER=[454]}
**Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:createEDIInvoices xmlns:ns2="http://xmlns.inspyrus.com/ExternalInspyrusService"><entityID>23 ....
16:47:18,662 INFO [org.apache.cxf.services.ExternalInspyrusService.ExternalInspyrusServicePort.ExternalInspyrusService] (default-workqueue-1) Inbound Message**
----------------------------
ID: 1
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml; charset=utf-8
Headers: {content-type=[text/xml; charset=utf-8], Date=[Sat, 02 Mar 2019 16:51:54 GMT], transfer-encoding=[chunked]}
Payload: <?xml version='1.0' encoding='UTF-8'?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header/><soap:Body><ns2:createEDIInvoices xmlns:ns2="http://xmlns.inspyrus.com/ExternalInspyrusService"><entityID>23</entityID><invoiceImport><header><captureProfile>EDI</captureProfile><currencyCode>USD</currencyCode><documentType>Invoice</documentType><flexAttribute10>Batch # 543421</flexAttribute10><flexAttribute12>543421</flexAttribute12><flexAttribute17>347.46</flexAttribute17><flexAttribute19>ST005.txt</flexAttribute19><flexAttribute21>120.46</flexAttribute21><flexAttribute22>16B286664984</flexAttribute22><flexAttribute23>EXLA ESTES EXPRESS LINES</flexAttribute23><flexAttribute24>W196-12965 </flexAttribute24><flexAttribute26>1% DISC 30 Days</flexAttribute26><flexAttribute27>2.22</flexAttribute27><flexAttribute28>February 17, 2019</flexAttribute28><flexAttribute29>February 16, 2019</flexAttribute29><flexAttribute3>125.00</flexAttribute3><flexAttribute30>1.00%</flexAttribute30><flexAttribute5>0454</flexAttribute5><flexAttribute6>DIRECT</flexAttribute6><flexAttribute8>SANAN*22449*383148735</flexAttribute8><invoiceDate>2019-01-17</invoiceDate><invoiceNumber>383148735</invoiceNumber><invoiceStatus>Open</invoiceStatus><invoiceTotalAmount>347.46</invoiceTotalAmount><leAddress1>PO BOX 9406</leAddress1><leCity>HAMPTON</leCity><leName>FERGUSON - HAMPTON</leName><lePostalCode>23670</lePostalCode><leState>VA</leState><orgID>0061</orgID><PONumber>L454-11593</PONumber><region>NA</region><scanDate>2019-01-17</scanDate><shipToAddress1>5206 W WATERS AVENUE</shipToAddress1><shipToCity>TAMPA</shipToCity><shipToName>FERGUSON 0196 TAMPA</shipToName><shipToPostalCode>33634</shipToPostalCode><shipToState>FL</shipToState><vendorAddress1>P.O Box 202893</vendorAddress1><vendorCity>DALLAS</vendorCity><vendorID>SANAN*22449~1~1</vendorID><vendorName>AS America, Inc.</vendorName><vendorNumber>22449</vendorNumber><vendorPostalCode>75320</vendorPostalCode><vendorSiteID>SANAN*22449~1~1</vendorSiteID><vendorState>TX</vendorState></header><lineItems><lineItem><lineNumber>1</lineNumber><lineType>ITEM</lineType><description># 5 FT AFR BATH LH OUT SOLAR WH</description><quantity>1</quantity><unitPrice>227.0000</unitPrice><lineTotalAmount>227.00</lineTotalAmount><UOM>EA</UOM><attribute4>0263212.020</attribute4><attribute5>A0263212020</attribute5></lineItem><lineItem><lineNumber>2</lineNumber><lineType>ITEM</lineType><description>Warranty</description><quantity>1</quantity><unitPrice>-4.5400</unitPrice><lineTotalAmount>-4.54</lineTotalAmount></lineItem></lineItems></invoiceImport></ns2:createEDIInvoices><soap:Fault><faultcode>F</faultcode><faultstring>Authorization Token not Found</faultstring></soap:Fault></soap:Body></soap:Envelope>
--------------------------------------
16:47:18,663 WARNING [org.apache.cxf.phase.PhaseInterceptorChain] (default-workqueue-1) Interceptor for {http://xmlns.inspyrus.com/ExternalInspyrusService}ExternalInspyrusService#{http://xmlns.inspyrus.com/ExternalInspyrusService}createEDIInvoices has thrown exception,
unwinding now: org.apache.cxf.interceptor.Fault:
Unexpected element {http://xmlns.inspyrus.com/ExternalInspyrusService}createEDIInvoices found.
Expected {http://xmlns.inspyrus.com/ExternalInspyrusService}createEDIInvoicesResponse.

Adding security headers in response using spring security

Am using spring security version 3.2. Am adding headers such as X-Frame-options, X-content-type-options in the response headers of the authenticated request.
<sec:http auto-config="false">
<sec:headers>
<sec:frame-options policy="DENY" />
<sec:content-type-options />
<sec:xss-protection enabled="true" block="true" />
</sec:headers>
</sec:http>
but those headers are not get adding in the security none request.
<sec:http security="none" pattern="/spring/loginpage" />
what might be the reason?
Because if there's no security on that pattern, then Spring Security isn't activated.
Make your own Interceptor, like this:
public class SecurityHeadersInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
response.setHeader("Strict-Transport-Security","max-age=31536000 ; includeSubDomains");
response.setHeader("X-Content-Type-Options", "nosniff");
response.setHeader("X-Frame-Options", "DENY");
response.setHeader("X-XSS-Protection", "1; mode=block");
response.setHeader("Content-Security-Policy", "default-src 'self'");
super.postHandle(request, response, handler, modelAndView);
}
}
In mvc-dispatcher-servlet.xml add:
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.example.interceptor.SecurityHeadersInterceptor"/>
</mvc:interceptor>
You should set Cache-Control: no-store, must-revalidate on any private responses too (incl if contains CSRF token, like a login form).

Using spring integration web service gateways with JMS

I need to implement a webservice client (and after that a webservice server) using spring-integration. I have already worked with the samples given by spring-integration team. That implementation uses HTTP as a transport layer. The client is making a HTTP request and server is sending back HTTP response. Instead of using HTTP I would like to use JMS as a transport layer. In this case client sends a SOAP-Request to a Queue (the server is listening to this queue) and while sending it also creates a temporary Queue and set that in the RepyTo in the JMS message header. Server gets receives the request from the Queue process it and then send back a SOAP-Response using the ReplyTo queue. I know we can do it using spring-ws and spring-jms libraries. I would like to do it using spring-integration support for ws and jms:
client sending request: java object -> Soap Message -> JMS message (payload is the SOAP xml)
server receiving request: JMS message (payload is the SOAP xml) -> Soap Message -> java object
server sending back response: java object -> Soap Message -> JMS message (payload is the SOAP xml)
For example I am giving xml configuration for webservice client that I am trying right now. Can you please check what I am missing?
<bean id="jndiEnvironment" class="java.util.Properties">
<constructor-arg>
<map>
<entry key="java.naming.factory.initial" value="value" />
<entry key="java.naming.provider.url" value="value" />
<entry key="java.naming.security.principal" value="value" />
<entry key="java.naming.security.credentials" value="value" />
</map>
</constructor-arg>
</bean>
<bean id="connectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="/my/jndi/name" />
<property name="jndiEnvironment" ref="jndiEnvironment" />
</bean>
<bean id="marshaller" class="org.springframework.oxm.jibx.JibxMarshaller">
<property name="targetClass" value="zahid.play.si.ws.jms.GetCountryDescriptionRequest" />
</bean>
<bean id="destinationResolver"
class="org.springframework.jms.support.destination.JndiDestinationResolver">
<property name="jndiEnvironment" ref="jndiEnvironment" />
</bean>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />
<bean id="messageSender" class="org.springframework.ws.transport.jms.JmsMessageSender">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destinationResolver" ref="destinationResolver" />
</bean>
<bean id="messageTemplate" class="org.springframework.integration.core.MessagingTemplate">
<property name="defaultChannel" ref="requestChannel" />
</bean>
<int:channel id="requestChannel" />
<ws:outbound-gateway id="wsClientGateway"
uri="jms:MY.TOPIC?messageType=TEXT_MESSAGE&deliveryMode=NON_PERSISTENT"
message-factory="messageFactory" marshaller="marshaller" unmarshaller="marshaller"
message-sender="messageSender"
request-channel="requestChannel" />
In the java code I am using: messagingTemplate.convertSendAndReceive(MessageBuilder.withPayload(request).build()) to send a request.
But I am getting this error:
[jms:MY.TOPIC?messageType=TEXT_MESSAGE&deliveryMode =NON_PERSISTENT] is not a valid HTTP URL
Solved the problem :) Here is the solution:
1) Define a destination provider for your Jms Uri:
public class JmsDestinationProvider implements DestinationProvider {
private String jmsUri;
public URI getDestination() {
if(StringUtils.hasText(jmsUri)){
try {
return new URI(jmsUri);
} catch (URISyntaxException e) {
}
}
return null;
}
public void setJmsUri(String jmsUri) {
this.jmsUri = jmsUri;
}
}
2) In the spring xml file add a bean for this destination provider and use that bean in ws:outbound-gateway
<bean id="jmsDestinationProvider" class="play.zahid.springint.activemq.ws.JmsDestinationProvider">
<property name="jmsUri" value="jms:test_queue?messageType=TEXT_MESSAGE&deliveryMode=NON_PERSISTENT" />
</bean>
<ws:outbound-gateway id="wsClientGateway"
destination-provider="jmsDestinationProvider"
message-factory="messageFactory" marshaller="marshaller" unmarshaller="marshaller"
message-sender="messageSender"
request-channel="requestChannel" />

How to change MediaType for MappingJacksonHttpMessageConverter in OAuth2RestTemplate

I have an application that is using Spring Source OAuth2 as s client to retrieve user data from a resource server and create a local user. I keep getting the error when the OAuth2ClientContextFilter tries to retrieve the token:
org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [org.springframework.security.oauth2.common.OAuth2AccessToken] and content type [application/x-javascript;charset=utf-8]
I understand the default MediaType is 'application/json' so I tried to customize the MappingJacksonHttpMessageConverter like this:
<bean id="jacksonConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<bean class="org.springframework.http.MediaType">
<constructor-arg value="application"/>
<constructor-arg value="x-javascript"/>
<constructor-arg value="UTF-8"/>
</bean>
</list>
</property>
</bean>
I also tried the 'ALL' constructor arg that is supposed to support */* content types but no luck. See http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/http/MediaType.html
Other important info is that I am using an all XML configuration right now. I just upgraded our 2.5 app to 3.1.1. I'm using OAuth2RestTemplate in a spring security PRE_AUTH filter, not in a controller. So I'm not using annotations to map the rest calls. I've tried adding <context:annotation-config/> but that didn't make a difference.
I'm simply calling my OAuth service bean from my custom AbstractPreAuthenticatedProcessingFilter. When the service bean tries to execute the rest call for user data, an exception is thrown, triggering the OAuth2ClientContextFilter which attempts to retrieve the token. Here's my OAuth2 service bean config:
<bean id="reprintsOauthService" class="com.coral.user.ReprintsOauthService">
<property name="reprintsUserInfoUrl" value="https://www.demo.com/api/userinfo.ashx" />
<property name="reprintsRestTemplate">
<bean class="org.springframework.security.oauth2.client.OAuth2RestTemplate">
<constructor-arg ref="reprintsResource"/>
<property name="messageConverters">
<list>
<ref bean="jacksonConverter"/>
</list>
</property>
</bean>
</property>
</bean>
Am I missing something? Why doesn't Jackson map the response?
Fixed! The problem was that the OAuth2RestTemplate isn't used for token retrieval. So I had to customize the org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport and add the MappingJacksonHttpMessageConverter to the existing method like this:
public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
this.messageConverters = new ArrayList<HttpMessageConverter<?>>(messageConverters);
this.messageConverters.add(new FormOAuth2AccessTokenMessageConverter());
this.messageConverters.add(new FormOAuth2ExceptionHttpMessageConverter());
MappingJacksonHttpMessageConverter jackson = new MappingJacksonHttpMessageConverter();
List<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(new MediaType("application", "x-javascript"));
jackson.setSupportedMediaTypes(mediaTypes);
this.messageConverters.add(jackson);
if(logger.isDebugEnabled())
{
logger.debug("*** Added custom media type 'application/x-javascript' to the Jackson converter");
}
}
I came to this question by searching for "oauth RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type"
your answer was the hint I needed to solve the problem! I added the following Maven dependencies (which are optional in the oauth2 pom):
<!-- spring oauth2 json dependency: -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.2</version>
</dependency>
When creating the AuthorizationCodeAccessTokenProvider i added the jackson message converter:
protected AuthorizationCodeAccessTokenProvider getAccessTokenProvider() {
final AuthorizationCodeAccessTokenProvider oAuthProvider = new AuthorizationCodeAccessTokenProvider();
final List<HttpMessageConverter<?>> messageConv = new ArrayList<HttpMessageConverter<?>>();
messageConv.add(new MappingJackson2HttpMessageConverter());
oAuthProvider.setMessageConverters(messageConv);
return oAuthProvider;
}

Spring LDAP - bind for successful connection

I'm trying to authenticate and then query our corporate LDAP using Spring LDAP and Spring security. I managed to make authentication work but when I attempt to run search I always get the following exception
In order to perform this operation a successful bind must be completed on the connection
After much research I have a theory that after I authenticate and before I can query I need to bind to connection. I just don't know what and how?
Just to mention - I can successfully browse and search our LDAP using JXplorer so my parameters are correct.
Here's section of my securityContext.xml
<security:http auto-config='true'>
<security:intercept-url pattern="/reports/goodbye.html"
access="ROLE_LOGOUT" />
<security:intercept-url pattern="/reports/**" access="ROLE_USER" />
<security:http-basic />
<security:logout logout-url="/reports/logout"
logout-success-url="/reports/goodbye.html" />
</security:http>
<security:ldap-server url="ldap://s140.foo.com:1389/dc=td,dc=foo,dc=com" />
<security:authentication-manager>
<security:authentication-provider ref="ldapAuthProvider">
</security:authentication-provider>
</security:authentication-manager>
<!-- Security beans -->
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://s140.foo.com:1389/dc=td,dc=foo,dc=com" />
</bean>
<bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg>
<bean class="foo.bar.reporting.server.security.ldap.LdapAuthenticatorImpl">
<property name="contextFactory" ref="contextSource" />
<property name="principalPrefix" value="TD\" />
<property name="employee" ref="employee"></property>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="foo.bar.reporting.server.security.ldap.LdapAuthoritiesPopulator" />
</constructor-arg>
</bean>
<!-- DAOs -->
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="contextSource" />
Here's code snippet from LdapAuthenticatorImpl that performs authentication. No problem here:
#Override
public DirContextOperations authenticate(final Authentication authentication) {
// Grab the username and password out of the authentication object.
final String name = authentication.getName();
final String principal = this.principalPrefix + name;
String password = "";
if (authentication.getCredentials() != null) {
password = authentication.getCredentials().toString();
}
if (!("".equals(principal.trim())) && !("".equals(password.trim()))) {
final InitialLdapContext ldapContext = (InitialLdapContext)
this.contextFactory.getContext(principal, password);
// We need to pass the context back out, so that the auth provider
// can add it to the Authentication object.
final DirContextOperations authAdapter = new DirContextAdapter();
authAdapter.addAttributeValue("ldapContext", ldapContext);
this.employee.setqId(name);
return authAdapter;
} else {
throw new BadCredentialsException("Blank username and/or password!");
}
}
And here's another code snippet from EmployeeDao with my futile attempt to query:
public List<Employee> queryEmployeesByName(String query)
throws BARServerException {
AndFilter filter = new AndFilter();
filter.and(new EqualsFilter("objectclass", "person"));
filter.and(new WhitespaceWildcardsFilter("cn", query));
try {
// the following line throws bind exception
List result = ldapTemplate.search(BASE, filter.encode(),
new AttributesMapper() {
#Override
public Employee mapFromAttributes(Attributes attrs)
throws NamingException {
Employee emp = new Employee((String) attrs.get("cn").get(),
(String) attrs.get("cn").get(),
(String) attrs.get("cn").get());
return emp;
}
});
return result;
} catch (Exception e) {
throw new BarServerException("Failed to query LDAP", e);
}
}
And lastly - the exception I'm getting
org.springframework.ldap.UncategorizedLdapException:
Uncategorized exception occured during LDAP processing; nested exception is
javax.naming.NamingException: [LDAP: error code 1 - 00000000: LdapErr:
DSID-0C090627, comment: In order to perform this operation a successful bind
must be completed on the connection., data 0, vece]; remaining name
'DC=TD,DC=FOO,DC=COM'
It looks like your LDAP is configured to not allow a search without binding to it (no anonymous bind). Also you have implemented PasswordComparisonAuthenticator and not BindAuthenticator to authenticate to LDAP.
You could try modifying your queryEmployeesByName() method to bind and then search, looking at some examples in the doc.
I'm going to accept #Raghuram answer mainly because it got me thinking in the right direction.
Why my code was failing? Turned out - the way I wired it I was trying to perform anonymous search which is prohibited by the system - hence the error.
How to rewire example above to work? First thing (and ugly thing at that) you need to provide user name and password of user that will be used to access the system. Very counterintuitive even when you login and authenticated, even if you are using BindAuthenticator system will not attempt to reuse your credentials. Bummer. So you need to stick 2 parameters into contextSource definition like so:
<bean id="contextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://foo.com:389/dc=td,dc=foo,dc=com" />
<!-- TODO - need to hide this or encrypt a password -->
<property name="userDn" value="CN=admin,OU=Application,DC=TD,DC=FOO,DC=COM" />
<property name="password" value="blah" />
</bean>
Doing that allowed me to replace custom implementation of authenticator with generic BindAuthenticator and then my Java search started working
I got the same error, couldn't find a solution.
Finally I changed the application pool identity to network service and everything worked like a charm.
(I have windows authentication and anonymous enabled on my site)

Resources