Spring Rest Controller - Can I allow allow CORS in the interceptor? - spring

I have an interceptor:
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestInterceptor());
}
Can I add my CORS configuration in the RequestInterceptor.java above? Like this:
public class RequestInterceptor extends HandlerInterceptorAdapter
{
...
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
...
...
if (request.getMethod().equals("OPTIONS")) {
response.setHeader("Access-Control-Allow-Origin", "http://localhost:9090");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
response.setHeader("Access-Control-Expose-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Max-Age", "4800");
}
...
return true;
}
}
I do not want any of these methods because for some reason they are not working for me:
#CrossOrigin
OR
WebMvcConfigurerAdapter -> addCorsMapping()
The only thing works for me is having a CORSFilter and mapping it in the web.xml. But that got me thinking that Interceptors are also kind of Filter, aren't they? Then why to add a Filter AND an interceptor when interceptor itself will do.
Anyway, I tried with the above piece of code and my requests are landing in the server and the rest controller method is running, but I see this response on my ReactJs page:
SEC7120: Origin http://localhost:9090 not found in Access-Control-Allow-Origin header.
SCRIPT7002: XMLHttpRequest: Network Error 0x80070005, Access is denied.
What am I missing? Is there anything else I have to do in the RequestInterceptor.java class for CORS?
Thanks.

Related

Spring Boot + CometD + CorFilter: An Authentication object was not found in the SecurityContext

My Project is Spring Boot 1.5.19 & Spring Security 4.2.6 & CometD 3.0.9.
In the UI side log is : CometD Subscribe Failed.
In the Services Back-end log is : org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
Location is CorsFilter.java : chain.doFilter(req, resp);
This error seems doesn't impact functions, login and handshake and meta/connect always OK. However, Cometd Subscribe Failed and the page always refresh then the new data will return. This isn't what I want. I want to achieve realtime data.I don't know the SecurityContext Error is the reason of this problem or not.
#Component
#Order(0)
public class CorsFilter implements Filter{
#Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) resp;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, resp);
}
}
#Override
public void destroy() {
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
Hope no errors and CometD realtime data run successfully.
It's just my fault.
In the CometdAuthenticator
#Autowired
private List<AbstractCometDSubscribeVoter> voters = Collections.emptyList();
add the #Autowired, everything works well.

Invalid CORS request in Spring

I am trying to enable certain IPs to access a particular method.
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/updateDetail")
.allowedOrigins("127.0.0.1", "10.96.33.45")
.allowedMethods("GET", "POST");
}
But when I am trying to call the same method I am getting invalid CORS request. Can anyone help me with this?
"Invalid CORS request" is returned by org.springframework.web.cors.DefaultCorsProcessor when
Spring is configured to use CORS and
browser sends "Origin" header with the request and it does not match with your server domain/port/scheme and
response does not have "Access-Control-Allow-Origin" header and
the request is not a preflight request.
If you don't need CORS, call cors().disable() in your implementation of WebSecurityConfigurerAdapter#configure(HttpSecurity http) (there might be other ways to do this, for example if you use Spring Boot).
Or you could add "Access-Control-Allow-Origin" header to your reponses using e.g. org.springframework.web.cors.CorsConfiguration or addCorsMappings (like you did, but maybe you should add more methods or url or IP doesn't match?).
This class is what are you looking for:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {
public SimpleCorsFilter() {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "12000");
response.setHeader("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
response.setHeader("Access-Control-Expose-Headers", "*");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
This filter will resolve all your cors issues
I was debugging problem like this for few days. The actual problem was that I had typo in my service uri name /data vs. /daba etc. This cause SpringBoot to fail to retrieve CORS configuration (even when I had /** mapping) so CORS-preflight got status 403, which caused browser not to make the actual request at all - if browser would have fired the request, Spring would have returned "Resource not found" and then I would have noticed the typo much faster.
if you started your application at localhost, the browser formulates the origin as null, so application will not get the origin localhost:8080 or 127.0.0.1, it will get null
I think changing 127.0.0.1 with null will fix your problem
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/updateDetail")
.allowedOrigins("null", "10.96.33.45")
.allowedMethods("GET", "POST");
}

Spring - Resteasy - Cors Double Access-Control-Allow-Origin header in response

I setup a web application with Spring 3 and Resteasy; since my resources require authentication I am not allowed to use * as Access-Control-Allow-Origin.
So I configured
org.jboss.resteasy.plugins.interceptors.CorsFilter
with the right origin domain.
This works with a desktop client (Paw for Mac Os and others), but not with the browser (Chrome); the problem is that the response contains a double value for Access-Control-Allow-Origin, that is the one I configured and '*'.
CorsFilter is not to blame because, even if you configure more than one origin, it always puts just one value for the header, the one which the request asked for.
I simply have no idea on who's putting that extra (and wrong) header, any idea on where I could look for?
Please note that the double header occurs on GET requests but not on OPTIONS requests.
I'm not sure where your doubled header comes from, but did you try to use custom filter?
e.g. :
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {
public SimpleCorsFilter() {
}
#Override
public void doFilter(ServletRequest req,
ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Authorization, Content-Type");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
I finally found out there is a proprietary MessageBodyWriterInterceptor in the classpath which does a wrong add header; now it's on me to remove that.
One thing I learned is that if something happens only when there is a body to write, a good starting point is surely the rendering pipeline
I've tried the following actions and it worked as a charm:
First, register the CorsFilter provider class in your web.xml:
<context-param>
<param-name>resteasy.providers</param-name>
<param-value>org.jboss.resteasy.plugins.interceptors.CorsFilter</param-value>
</context-param>
By doing so, your server is already enabled to handle CORS requests, however, you need to add some allowed origins to get it working, therefore, you should get access to the CorsFilter's instance, which was created by RestEasy then add all the URLs you wish to grant access to or add a * if you wish to grant access to any.
In this regard, if you're using RestEasy Spring Integration, you'll need to grab an instance of the org.jboss.resteasy.spi.ResteasyProviderFactory class by autowiring it into your code:
#Autowired
private ResteasyProviderFactory processor;
then use a setup method annotated with #PostConstruct to get the instance of the CorsFilter from the ResteasyProviderFactory like the code snippet below:
#PostConstruct
public void setUp() {
ContainerRequestFilter[] requestFilters = processor.getContainerRequestFilterRegistry().preMatch();
CorsFilter filter = (CorsFilter) Iterables.getLast(Iterables.filter(Arrays.asList(requestFilters), Predicates.instanceOf(CorsFilter.class)));
filter.getAllowedOrigins().add("*");
}
P.S.: I'm using this frameworks:
Spring Framework 3.2.18.RELEASE
RestEasy 3.0.12.Final
I hope it helps!
For those struggling like me who don't use the Application starter but only Spring + Reasteasy.
Just add in web.xml:
<context-param>
<param-name>resteasy.providers</param-name>
<param-value>package.to.your.cors.CORSFilter</param-value>
</context-param>
And create the Java Class CORSFilter like
package package.to.your.cors;
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
#Provider
public class CORSFilter implements ContainerResponseFilter {
#Override
public void filter(final ContainerRequestContext requestContext,
final ContainerResponseContext cres) throws IOException {
cres.getHeaders().add("Access-Control-Allow-Origin", "*");
cres.getHeaders().add("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
cres.getHeaders().add("Access-Control-Allow-Headers", "X-Auth-Token, Content-Type");
cres.getHeaders().add("Access-Control-Max-Age", "4800");
}
}
It works like a charm.
PS: inspired by s_bighead answer but I could not comment his answer to add my details.

Spring 4.2.2 CORS Support not working

#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
}
I'm following the guide in https://spring.io/blog/2015/06/08/cors-support-in-spring-framework and added the above code to my WebConfig
However, CORS is not working, log show
20151103 183823 [http-bio-8080-exec-3] WARN org.springframework.web.servlet.DispatcherServlet (DispatcherServlet.java:1136) - No mapping found for HTTP request with URI [/my/url] in DispatcherServlet with name 'dispatcher'
I can successfully call my api without CORS(same domain), so it is not the api problem.
In Chrome console, log show
XMLHttpRequest cannot load http://localhost:8080/my/url.
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:8100' is therefore not allowed access. The response had HTTP status code 404.
Just add this filter for CROS in package which is contain other configuation
#Component
public class RequestFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
}
It will work fine for you.

SimpleCORSFilter not working

I am using Spring Rest as my backend, when I sent request by $.ajax{}, I got error message:
XMLHttpRequest cannot load http://121.40.249.129:8080/user/login. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8000' is therefore not allowed access.
So, I added SimpleCORSFilter in my Spring Project:
SimpleCORSFilter:
#Component
public class SimpleCORSFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version");
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig filterConfig) {}
#Override
public void destroy() {}
}
The since I don't have web.xml, so I didn't add the code to web.xml:
<filter>
<filter-name>simpleCORSFilter</filter-name>
<filter-class>xxx.xxx.SimpleCORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>simpleCORSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
So, I still get the error, how can I fix it.
As I have experienced, * won't work for Access-Control-Allow-Origin when Access-Control-Allow-Credentials is set to true.
So, you should instead have
response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8000");
Actually, instead of hardcoding the url, you can have that as a property. If you want to allow multiple origins, this answer would help.
Also:
It's advised to annotate the class with #Order(Ordered.HIGHEST_PRECEDENCE), because this filter should come first.
If you are using CSRF, the corrosponding header should also be added to the list in Access-Control-Allow-Headers.
Update:
As #RTzhong did (see his comments below), replacing * with request.getHeader("Origin") seems like the ideal fix. However, a better security practice in general would be to specify the actual url or first checking with a whitelist, unless one must expose his API publicly to unknown websites.
Refer to Spring Lemon's source code for a concrete example.

Resources