CORS issue on java web application - javascript

The backend is developed by Spring MVC and hosted in tomcat.
When I use postman dev tool in Chrome to test the endpoint (Restful), everything is working fine. The "Access-Control-Allow-Origin", "*" is successfully added in the server. like below screenshot
But when I wrote the Ajax to make the same request. Because browser has the Origin Policy (postman has no this policy), it will first send an HTTP OPTIONS request header to the server to determine whether the actual request is safe to send. like below screenshot.
The response header is quite different to the one I got from postman, and there is no "Access-Control-Allow-Origin", "*" in the header. I think the server just can't accept the options request.
I have tried to add the filter in tomcat web.xml by looking this link: http://enable-cors.org/server_tomcat.html
It's Only working perfectly by using dev tool like curl and postman which has no Origin policy. But it's not working on the browser by Ajax.
Please see the below filter class
public class SimpleCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
System.out.println("test123");
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
I use the System.out.println("test123"); in the filter, when using postman or curl, it printed the 'test123' in the console, but didn't print when using browser, so I guess the browser send the OPTIONS request, and the filter didn't intercept it, or other things intercept the request from browser first?
Can anyone help me please? Thanks.

If you are using tomcat you can have tomcat's implementaion in your web.xml:
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Or In my application I did a filter(updated) like below:
public class CorsFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, HEAD, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
if ("OPTIONS".equalsIgnoreCase((HttpServletRequest) servletRequest.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(servletRequest, response);
}
}
#Override
public void destroy() {
}
}
Also please note that you should not annotate filter class with Spring's annotations like #Component
Or better you can go with Spring's CORS support

Using an response.setHeader("Access-Control-Allow-Origin", "*");
Will not work.
You will need to put the explicit requesting URL.
Take the origin from the request to the servlet and dynamically replace on each request.
Here's a code snippet that I use to solve this problem:
String clientOrigin = request.getHeader("origin");
response.setHeader("Access-Control-Allow-Origin", clientOrigin);
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Access-Control-Allow-Origin,CopyStreamException,Access-Control-Allow-Methods,Access-Control-Max-Age");
EDIT 1:
I've looked at my code again, and I saw that I also added filters to my web.xml as mentioned by #Tom-Sebastian
Here what I have in the web.xml:
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORS</filter-name>
<servlet-name>myServlet</servlet-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Hope it will help,
Liron

Related

Response for preflight has invalid HTTP status code: 401 angular

Using angular and Spring Boot we're trying to add authentication to our service but for some reason we can't 'open' and fetch data from an url we know works
Angular:
this.getMismatches = function () {
return $http({
"async": true,
"crossDomain": true,
"url": GLOBALS.mismatchUrl,
"method": "GET",
"headers": {
"authorization": "Basic YWRtaW46USNROawdNmY3UWhxQDlQA1VoKzU="
}
});
}
(currently the login token is hard coded for testing purposes)
Rest service:
#CrossOrigin(origins = "*")
#RequestMapping("/api/mismatch")
public List<Mismatch> home() {
return service.getAll();
}
CrossOrigin = * should take care of the CORS issue but this failed URL call is really weird.
Extra things we've tried:
'Access-Control-Allow-Methods', 'GET, POST, OPTIONS'
'Access-Control-Allow-Origin', '*'
'Content-Type', json plaintext jsonp etc
App.js:
$httpProvider.defaults.headers.common = {};
$httpProvider.defaults.headers.post = {};
$httpProvider.defaults.headers.put = {};
$httpProvider.defaults.headers.patch = {};
You have mentioned in your comments that by using postman you can get the response as expected. That is a good starting point. I suspect that by using the curl command curl -i -X URL from the terminal also returns the correct response.
If postman works correctly, you have to be aware by the fact that right before making a request angular sends another request, called pre-flight request, which does a minimal check to the endpoint at the server side.
This request is an OPTIONS type request.
First, you have to make sure that your dispatcherServlet accepts OPTIONS requests. You can achieve this either by specifying it in a *.properties configuration file , such as:
spring.mvc.dispatch-options-request=true
or by configuring web.xml
<servlet>
<!--content eluded for clarity-->
<init-param>
<param-name>dispatchOptionsRequest</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
After you have configured it to accept OPTIONS requests, create a Filter.java and configure a CORS filter.
You can guide by the following example:
public class CorsFilter implements Filter{
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
if(response instanceof HttpServletResponse){
HttpServletResponse alteredResponse = ((HttpServletResponse)response);
addCorsHeader(alteredResponse);
}
filterChain.doFilter(request, response);
}
private void addCorsHeader(HttpServletResponse response){
//TODO: externalize the Allow-Origin
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, HEAD");
response.addHeader("Access-Control-Allow-Headers", "Authorization, X-PINGOTHER, Origin, X-Requested-With, Content-Type, Accept");
response.addHeader("Access-Control-Max-Age", "1728000");
}
#Override
public void destroy() {}
#Override
public void init(FilterConfig filterConfig)throws ServletException{}
}
In the end, don't forget to add this filter in web.xml along with the following init-params.
<filter>
<filter-name>cors-filter</filter-name>
<filter-class>ai.surge.usrmngmtservice.util.cors.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>*</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.methods</param-name>
<param-value>GET,POST,OPTIONS,PUT</param-value>
</init-param>
<init-param>
<param-name>cors.exposed.headers</param-name>
<param-value>Authorization,Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
</init-param>
<!--<init-param>-->
<!--<param-name>cors.preflight.maxage</param-name>-->
<!--<param-value>1800</param-value>-->
<!--</init-param>-->
</filter>
You should be ready to go now.

CORS not working in standalone tomcat

I am developing a web application in spring boot and I retrieve the data from the database and produce a Json list and send it to a url(Rest Web Services).Then I get it from a get request in the Javascript using getJSON.
$.getJSON(
'http://localhost:8080/dataurl?i=1',
function(data) {
}
But it works fine with embedded tomcat server and does not work with standalone tomcat server.The error I get is
It is only returning a empty JSON array when I access the url in browser.
Why is it not working in standalone tomcat server.Is it also possible to add oauth2 security to this?Any help is appreciated.
If you want to enable CORS you should create filter.
#Component
public class CorsConfig implements Filter {
private final Logger log = LoggerFactory.getLogger(CorsConfig.class);
public CorsConfig() {
log.info("SimpleCORSFilter init");
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me, Authorization");
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
This will add necessary headers for CORS.
The problem was the with postgresql maven dependency.When I changed the dependency it worked.Hope it helps anyone

X-Content-Type-Options and Content-Type Headers, not working together

In my web application I used owasp ZED Tool to check security vulnerability. I found that X-Content-Type-Options and Content-Type headers are not set.
In my cross filter, I have added the headers in HTTPResponse object
#WebFilter("/*")
public class CrossFilter implements Filter {
private static CoreLogger logger=LoggerFactory.getLogger(FirstServlet.class);
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filter) throws IOException,
ServletException {
try{
HttpServletResponse httpServletResponse = (HttpServletResponse ) servletResponse ;
httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
httpServletResponse.setHeader("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
httpServletResponse.setHeader("Access-Control-Max-Age", "1209600");
httpServletResponse.setHeader("X-Frame-Options", "SAMEORIGIN");
httpServletResponse.setHeader("X-XSS-Protection", "1; mode=block");
httpServletResponse.setHeader("X-Content-Type-Options", "nosniff");
httpServletResponse.setHeader("Content-Type", "text/plain");
filter.doFilter(servletRequest, servletResponse);
}catch(Exception exc){
logger.info(exc.getMessage());
if(logger.isDebugEnabled()){
logger.error(exc.getMessage(),exc);
}
}
}
Now when I am trying to access the application over browser, it shows me the source of HTML . I thought I have added text/plain was wrong and I tried text/html , but still error persists.
Could anyone help me out if the way I am doing is wrong ?

JSESSIONID cookie not stored

I have a frontend written in angular which runs on localhost:3002.
I have a backend written with Spring-boot which runs on localhost:8080.
I added a filter to handle CORS (which I found on SO and adapted to my need) :
#Component
public class CORSFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "http://localhost:3002");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
chain.doFilter(request, response);
}
public void destroy() {}
}
When I authenticate, I can see that the server sends a cookie :
This cookie is not sent with the following requests. This results in 401 responses :
I looked at the "resources" panel of the chrome console and found out that no cookie were stored.
Any ideas?
In the file where you configure your $httpProvider, add this line to specify you want to send credentials with cross-site requests:
$httpProvider.defaults.withCredentials = true;
Typically, AngularJS will automatically send these cookies with requests (for same-origin requests) but since your requests are cross-site then you have to manually configure this.

Preflight OPTIONS request to SOAP service does not work

What I want to do: Call a cross-domain SOAP-Service from JavaScript using jQuery with the jQuery Soap plugin (by Remy Blom). (that is, I call $.soap(); in JavaScript)
What I did: CORS Setting on the server side (CXF) are working (using the org.eclipse.jetty.servlets.CrossOriginFilter), so the following is present in the answer:
Access-Control-Allow-Head... X-Requested-With,Content-Type,Accept,Origin
Access-Control-Allow-Meth... GET,POST,OPTIONS,HEAD
Access-Control-Allow-Orig... http://localhost:8082
Content-Type application/soap+xml;charset=UTF-8
What is missing: Firefox and Chrome send preflight OPTIONS requests prior to the POST request for the SOAP call. Obviously SOAP does not allow the OPTIONS verb.
It does not work with SoapUI (5.0) as well as CXF (2.7.7). It is even stated in a comment in org.apache.cxf.binding.soap.interceptor.ReadHeadersInterceptor line 130ff:
/*
* Reject OPTIONS, and any other noise that is not allowed in SOAP.
*/
So, my question is: How can I modify my SOAP servcie implementation (using CXF), such that the OPTIONS request returns successfully?
Even if it's a little bit late, I had the same problem recently and maybe it will help future travelers.
In the case of an OPTIONS request you may not continue with the FilterChain.
I created a simple CORSFilter, which looks like this:
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*
public class CORSFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
resp.addHeader("Access-Control-Allow-Origin", "*");
resp.addHeader("Access-Control-Allow-Methods", "GET, POST");
resp.addHeader("Access-Control-Allow-Headers", req.getHeader("Access-Control-Request-Headers"));
if (!req.getMethod().equalsIgnoreCase("OPTIONS")) {
chain.doFilter(request, response)
}
}
#Override
public void destroy() {}
}
And I added the following to my web.xml:
<filter>
<filter-name>CORSFilter</filter-name>
<filter-class>CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CORSFilter</filter-name>
<url-pattern>/api/*</url-pattern>
</filter-mapping>

Categories