Moving Spring Application from Glassfish to Tomcat - ajax

I've been working on Spring 3.0 web application using Netbeans and Glassfish server. I'm now to the point where I should be deploying the application for public use, however in attempting to move from Glassfish to Tomcat I'm running into an error. My application uses AJAX to grab information from a URL within the application but when I request the url I get the following:
The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers ().
My controller for the information it is requesting looks like so:
#RequestMapping(value = "/electricity/usage/")
public #ResponseBody List<UsageData> getEUsage(HttpSession session) {
UsageDataDAO UsageDAO = new UsageDataDAO();
User u = (User) session.getAttribute("user");
List<UsageData> l = UsageDAO.getAllUsageData(u.getAccountNum(), 'e');
return l;
}
Why is this happening when I migrate to Tomcat?

This error happens because what your browser sends to the server in the "Accept" header does not match what the server can return from the URL /electricity/usage/
If you are using a library like jQuery (and NOT doing cross-domain requests, this is important) then your browser will send this accept header:
Accept: application/json
You getting that error means that the server does not think the URL /electicity/usage can return a JSON response. This is configured in your webmvc-config.xml file. This is what I have in mine related to JSON requests:
<!-- allows rendering responses in XML and JSON formats -->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver" p:order="1">
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="marshaller">
<bean class="org.springframework.oxm.xstream.XStreamMarshaller" p:autodetectAnnotations="true"/>
</property>
</bean>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/>
</list>
</property>
</bean>
See this tutorial for implementing JSON responses in Spring MVC: http://rwehner.wordpress.com/2010/06/09/2-ways-to-create-json-response-for-ajax-request-in-spring3/
Why this code works on Glassfish, but not Tomcat - that is a good question...
If you are doing a cross-domain request, then this topic applies: JQuery's getJSON() not setting Accept header correctly?

Related

Empty SignatureValue and DigestValue in SOAP Request using Wss4jSecurityInterceptor

I am updating the client of an existing SOAP webservice since the service provider has made some changes to thier security headers in the request.
The requirement is to digitally sign the Timestamp which should be present in the request header and the body should not be digitally signed. I am using XML config to create my SOAP request header and digitally sign the Timestamp.
I am basically using org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor as an interceptor. The problem is that Timestamp gets created in the request header however the SignatureValue and DigestValue tags are empty
I have referred https://docs.spring.io/spring-ws/site/reference/html/security.html#security-wss4j-digital-signatures
Versions:
Spring-ws-core --> 2.0.0.RELEASE
spring-ws-security --> 2.0.0.RELEASE
<bean id="wsClientSecurityInterceptor" class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
<property name="securementActions" value="Timestamp Signature"/>
<property name="securementSignatureKeyIdentifier" value="DirectReference" />
<property name="securementUsername" value="username" />
<property name="securementPassword" value="keystorepassword" />
<property name="securementSignatureCrypto" ref="clientCrypto"/>
<property name="securementSignatureUser" value="username" />
<property name="securementSignatureParts" value="{}{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp"/>
</bean>
<bean id="clientCrypto" class="org.springframework.ws.soap.security.wss4j.support.CryptoFactoryBean">
<property name="keyStorePassword" value="keystorepassword" />
<property name="keyStoreLocation" value="file:${key.store.location}"/>
<property name="keyStoreType" value="jks" />
<property name="keyStoreProvider" value="IBMJCE" />
</bean>
Though the timestamp gets added to the wsse:Security element in the header, the DigestValue and SignatureValue elements belonging to the xmlns:ds="http://www.w3.org/2000/09/xmldsig#" namespace are always empty
This does not happen if I only sign the Body
I also tried using another Interceptor XwsSecurityInterceptor but that does not work without the Wss4jSecurityInterceptor and gives me the same result when used with Wss4jSecurityInterceptor
<bean id="xwsSecurityInterceptor" class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="classpath:securityPolicy.xml"/>
<property name="callbackHandlers">
<list>
<ref bean="keyStoreHandler"/>
</list>
</property>
</bean>
<bean id="keyStoreHandler" class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
<property name="keyStore" ref="keyStore"/>
<property name="privateKeyPassword" value="keystorepassword"/>
</bean>
<bean id="keyStore" class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
<property name="location" value="file:${key.store.location}"/>
<property name="password" value="keystorepassword"/>
</bean>
After referring a number of IBM support posts, I have finally come to the solution and have received a QA signoff too on the implementation. I had to replace all my spring mvc config for creating SSL context with configurations on the WAS server itself.You can configure all this using either scripts or using the WAS console manually.
Steps:
Add an entry to ur applications web.xml.Please refer this post https://www.ibm.com/support/knowledgecenter/en/SSAW57_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/twbs_jaxwsclientdd.html
The link below will give you a step by step guide as to how to configure the request payload to have a signed timestamp on WAS server.
http://pglezen.github.io/was-config/html/signts.html#sec.signts.keystores.consumer
It basically focusses on :
Policy Set Creation:This will Specify the element or elements to be signed or encrypted in this message part.In our case its the timestamp in the header
Client Policy Set Bindings. If you are the provider you have to refer the provider Policy set bindings section.This involves creation of truststore and keystore required to configure your SSL context. Also has configurations for any proxy settings that you might require.
Finally take a JNDI reference of the service whereever you need to call a particular operation in the service.Refer https://www.ibm.com/support/knowledgecenter/SSAW57_9.0.5/com.ibm.websphere.nd.multiplatform.doc/ae/twbs_jaxwsclientdd.html
Once this done, Simply navigate to Services--> Service Clients and you should have your service refs visible there. You should now attach the Policy set and the bindings created above to the Service client references created in WAS due to point 1 above.You might have to write a script to attach the polict set and binding since after deployment you dont want to do this step manually
Please note: this solution came into picture sicne the version of WAS 8.5.5.16,spring -3.0.5.RELEASE and spring-ws-2.0.0.RELEASE did not support signing of timestamp in the request payload of the SOAP service request. Hope this helps someone !!

ADFS integration using Spring SAML -SP metadata vs IDP metadata?

I implemented the Spring SAML sample application using ssocircle and it worked fine. Now I have been trying to implement it for the client's ADFS. Following is the configuration I think that is required, please correct me if I am wrong:
Change the first parameter below, to the federationMetadata.xml url provided by client
<bean class="org.opensaml.saml2.metadata.provider.HTTPMetadataProvider">
<constructor-arg>
<value type="java.lang.String">http://idp.ssocircle.com/idp-meta.xml</value>
</constructor-arg>
<constructor-arg>
<value type="int">5000</value>
</constructor-arg>
<property name="parserPool" ref="parserPool"/>
</bean>
Replace the entity id of SP metadata below:
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="entityId" value="replaceWithUniqueIdentifier"/>
<property name="extendedMetadata">
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="signMetadata" value="false"/>
<property name="idpDiscoveryEnabled" value="true"/>
</bean>
</property>
</bean>
I haven't been able to figure out the following:
All I have received is a url to adfs/../federationMetadata.xml, who is supposed to create the SP metadata?
Am I supposed to create SP metadata and provide to the client, to add it in adfs? Because, that's what I did using sample application. I added the generated metadata to ssocircle
Is my understanding, that point 1 would be adfs url, and point 2 will be SP entity id, correct?
I would be grateful if you could clarify the above to me, also if possible, point me to straightforward tutorial that helps in integrating SAML with Spring security enabled application as I haven't been able to find the same.
Many thanks
To make SAML between SP and IdP (ADFS) work, you have to mutually exchange metadata.
The ADFS metadata are available on the URL https://adfs-host/FederationMetadata/2007-06/FederationMetadata.xml and you can register them in your SP either with HTTPMetadataProvider, or download them and read them from classpath, or file system with ResourceBackedMetadataProvider.
For SP metadata, you have to configure MetadataGenerator (as you have it in your question) and then expose it via FilterChainProxy. Here is a Java configuration (it's equivalent for XML):
#Bean
public FilterChainProxy samlFilter() throws Exception {
List<SecurityFilterChain> chains = new ArrayList<SecurityFilterChain>();
chains.add(new DefaultSecurityFilterChain(
new AntPathRequestMatcher("/saml/metadata/**"), metadataDisplayFilter()));
return new FilterChainProxy(chains);
}
Than, you can access SP metadata on the URL https://sp-host/saml/metadata and register them on ADFS as a Relying Party Trust. Again, you can do this either via URL, or import data from the (downloaded) file.
Basically, you should be fine if you follow Spring Security SAML Reference Documentation which uses XML configuration. In case, you'll need to switch to Java configuration, you can find handy either referenced vdenotaris/spring-boot-security-saml-sample, or my working prototype sw-samuraj/blog-spring-security.

Spring SAML and ADFS 2.0

I used Spring SAML Sample application and followed the instructions. My configuration worked perfectly as expected, when SSOCircle IDP was used. However, I wanted to work this with ADFS. So, I followed the instructions on how to configure Spring SAML with ADFS. I got it through where when I access Spring SAML application is invoked, it displays the IDP Selection page with URL to adfs/services/trust. When I click on it, it prompts me for AD authentication, which is what I expected. But, when I provide the user id/password for the AD authentication, it process it and displays a message that reads "page can't be displayed".
On the address bar, the url to the page is displayed as:
https://localhost:8443/spring-security-saml2-sample/saml/login?idp=http%3A%2F%2FTest-DC.TEST.local%2Fadfs%2Fservices%2Ftrust.
Test-DC.TEST.local is my server where ADFS and AD is hosted.
There are no errors on the tomcat log or anywhere.
could someone who has setup Spring SAML with ADFS help here please?
Make sure that you're using SHA2 and not SHA1.
either override the afterPropertiesSet method:
public class SSOConfigBean
implements InitializingBean
{
private String signatureAlgorithmSHA = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256;
private String digestAlgorithmSHA = SignatureConstants.ALGO_ID_DIGEST_SHA256;
#Override
public void afterPropertiesSet() throws Exception
{
BasicSecurityConfiguration config = (BasicSecurityConfiguration) Configuration.getGlobalSecurityConfiguration();
config.registerSignatureAlgorithmURI("RSA", signatureAlgorithmSHA);
config.setSignatureReferenceDigestMethod(digestAlgorithmSHA);
}
}
and add this to your securityContext:
<!-- setting encryption to SHA2 instead of default SHA1 -->
<bean class="path.to.SSOConfigBean"/>
Or update the securityContext you're using while setting your SP metadata as below:
<bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<constructor-arg>
<bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<property name="entityId" value="urn:samltest"/>
<property name="extendedMetadata">
<bean class="org.springframework.security.saml.metadata.ExtendedMetadata">
<property name="idpDiscoveryEnabled" value="false"/>
<property name="local" value="true"/>
<property name="signingAlgorithm" value="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
</bean>
</property>
</bean>
</constructor-arg>
</bean>

What is the purpose of conversion-service and content-negotiation-manager in Spring?

What is the purpose of conversion-service and content-negotiation-manager in Spring? We have this in our Spring appContext but I am not sure about its purpose.
For content-negotiation-manager:
I have read here:
http://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-config-content-negotiation that the content-negotiation-manager acts like a 'resolver' for #RequestMapping - e.g. if my mapping URL is "/person/create" - it will be called when the client accesses /person/create.json and /person/create.html (given the configuration below).
I am also able to access /person/list.xml and it returns an xml result even if xml is not defined in the content-negotiation-manager since I have the Jackson in my classpath:
For file extensions in the request URI, the MVC Java config and the
MVC namespace, automatically register extensions such as .json, .xml,
.rss, and .atom if the corresponding dependencies such as Jackson,
JAXB2, or Rome are present on the classpath.
So, we defined the content-negotiation-manager since we support html, and it is not mapped by default. Is my understanding correct?
For conversion-service:
In our classes, we have an ObjectMapper.readValue(json, Obj.class) and #RestController returning an object in xml/json format depending on the request (it returns an xml format if you access /list.xml and returns json format when you access /list.json). But I've read that #RestController can work without the conversion-service. So I am not sure why it is supplied in the <mvc:annotation-driven>
<mvc:annotation-driven conversion-service="conversionService" content-negotiation-manager="contentNegotiationManager"/>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="html" value="text/html"/>
</map>
</property>
<property name="defaultContentType" value="application/json"/>
</bean>
Both the conversion service and the content negotation manager are always used by Spring MVC, regardless whether you define them or not. You can configure them according to your needs. That's the case here.
E.g. name="defaultContentType" value="application/json" means that if the client doesn't prefer a specific media type the server should send back JSON.

Spring MVC "redirect:" prefix always redirects to http -- how do I make it stay on https?

I solved this myself, but I spent so long discovering such a simple solution, I figured it deserved to be documented here.
I have a typical Spring 3 MVC setup with an InternalResourceViewResolver:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
I have a pretty simple handler method in my controller, but I've simplified it even more for this example:
#RequestMapping("/groups")
public String selectGroup() {
return "redirect:/";
}
The problem is, if I browse to https://my.domain.com/groups, I end up at http://my.domain.com/ after the redirect. (In actuality my load-balancer redirects all http requests to https, but this just causes multiple browser alerts of the type "You are leaving/entering a secure connection" for folks who have such alerts turned on.)
So the question is: how does one get spring to redirect to https when that's what the original request used?
The short answer is, set the InternalResourceViewResolver's redirectHttp10Compatible property to false:
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
<property name="redirectHttp10Compatible" value="false" />
</bean>
You could do this on a per-request basis instead, by having your handler method return View instead of String, and creating the RedirectView yourself, and calling setHttp10Compatible(false).
(It turns out the culprit is HttpServletResponse.sendRedirect, which the RedirectView uses for HTTP 1.0 compatible redirects, but not otherwise. I guess this means it's dependent on your servlet container's implementation (?); I observed the problem in both Tomcat and Jetty.)
Spring Boot provides this nice configuration based solution to this if you're running behind a proxy server, simply add these two properties to your application.properties file:
server.tomcat.remote_ip_header=x-forwarded-for
server.tomcat.protocol_header=x-forwarded-proto
This works for me deploying the otherwise unchanged spring-boot-sample-web-ui to Elastic Beanstalk behind an https load balancer. Without these properties the redirect on line 68 of the MessageController defaults to http and hangs.
Hope this helps.
What worked for me is adding this to application.properties server.tomcat.use-relative-redirects=true
So when you have:
public function redirect() {
return "redirect:/"
}
Without the server.tomcat.use-relative-redirects it will add a Location header like: http://my-host.com/.
With the server.tomcat.use-relative-redirects it will look like: /.
So it will be relative to the current page from browser perspective.
Are you sure?
Looking at the code it seems there is no difference. Both variants use the encoded url, see the sendRedirect method in RedirectView:
String encodedURL = isRemoteHost(targetUrl) ? targetUrl : response.encodeRedirectURL(targetUrl);
if (http10Compatible) {
// Other if/else stuff depending on status code
// Send status code 302 by default.
response.sendRedirect(encodedURL);
}
else {
HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl);
response.setStatus(statusCode.value());
response.setHeader("Location", encodedURL);
}
I had the same problem, but it was triggered by setting up tomcat behind a loadbalancer. The loadbalancer does the SSL handshake and forwards to tomcat a plain http connection.
Solution would be to send a special Http Header in your Loadbalancer, so tomcat can "trust" this connection. Using a servlet filter should set response.isSecure flag. Then overwrite RedirectView to see if response.isSecure and handle it the right way.
I kept this solution short because i am not sure if it machtes the question.
Since Spring Boot 2.1 you have to add the following configuration to your application.properties:
server.use-forward-headers=true
or application.yml:
server:
use-forward-headers: true
if you use springmvc ,you can try the following:
modelAndView.setView(new RedirectView("redirect url path", true, false));
I add scheme="https" in file server.xml for connector with port="80":
<Connector port="80" protocol="HTTP/1.1" URIEncoding="UTF-8"
connectionTimeout="20000" redirectPort="443" scheme="https" />
This started happening for us on Chrome 87 (https://blog.chromium.org/2020/08/protecting-google-chrome-users-from.html), for a quickfix to avoid the warning page in our springboot app we solve it by adding use-relative-redirects: true in the application.yml.
I was also facing same issue...When redirecting it goes to http instead of HTTPS , below changes done :
RedirectView redirect = new RedirectView("/xyz",true);
redirect.setExposeModelAttributes(false);
redirect.setHttp10Compatible(false);
mav = new ModelAndView(redirect);

Resources