We have used spring boot to write RESTFul web services. There are tons of web services in our application. And the new requirement is here now. My boss asked me to track each service request.
For example : If a login service is invoked, it has to be tracked with count, response status and few more details. Is there a way to that. Something like interceptor which will be invoked after the service is processed and just before the response is served.
Spring Boot supports servlet filters and allows you to register them the same way you would any #Component.(By having the class annotated with #Component and placing it in a package where Spring Boot will auto-configure the class)
Use the (ServletRequest req, ServletResponse res) objects to retrieve the data you require.
package application.basepackage.maybeafilterspecificpackage;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
#Component
public class SimpleLoggingFilter implements Filter {
private final Logger logger = LoggerFactory.getLogger(SimpleLoggingFilter .class);
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest request = (HttpServletRequest) req;
String url = request.getServletPath();
logger.info(url + " - " + request.getMethod());
chain.doFilter(req, res);
logger.info(url + " - " + response.getStatus());
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
I created a #RequestMapping mapped to /test where it simply returns a String below I clean up my logs to be more readable.
This first time I had the method throw an exception
/test - GET
/test - 500
/favicon.ico - GET
/favicon.ico - 200
Then i fixed it to simply return a string
/test - GET
/test - 200
/favicon.ico - GET
/favicon.ico - 200
You spoke as though you need a little more power over your logs but if you are using embedded Tomcat then a more simple alternative is.
Spring Boot enable http requests logging
I think the right tool for you is Spring Boot Actuator, especially the /metrics url which tracks requests (and response codes) of every endpoints of your application (among other things) :
https://spring.io/guides/gs/actuator-service/
http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-metrics.html
Related
Is there any way to log complete HTTP exchanges (request + response including headers + payloads) in a spring-web REST service?
I have seen the CommonsRequestLoggingFilter, but that only logs the request. Is there a matching CommonsResponseLoggingFilter? Or a different solution?
In Jersey this functionality is provided by LoggingFeature, you just need to enable it.
For the HTTP server I use the default Tomcat. There's AccessLogValve, but that doesn't log the payload.
Ideally I would want something at spring-web level, similar to Jersey, so I don't have to worry about it if I switch from Tomcat to Jetty or Undertow.
I am not sure if spring have any build-in filter which help to log both request/response.
But you can write customize filter to do it.
#Component
public class CustomLoggingFilter extends GenericFilterBean {
#Override
public void doFilter(
final ServletRequest req,
final ServletResponse res,
final FilterChain chain)
throws IOException, ServletException {
CachedBodyHttpServletRequest reqWrap = new CachedBodyHttpServletRequest(
(HttpServletRequest) req);
ContentCachingResponseWrapper resWrap = new ContentCachingResponseWrapper(
(HttpServletResponse) res);
chain.doFilter(reqWrap, resWrap);
resWrap.copyBodyToResponse();
}
}
CachedBodyHttpServletRequest and ContentCachingResponseWrapper can help you to access headers/datas multiple time and do few logging without broken any datas.
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/util/ContentCachingRequestWrapper.html
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/util/ContentCachingResponseWrapper.html
We have a Keycloak server that is securing our Spring Boot application. That works fine so far. However we now need a forgot password page, which has to be reachable without login of course. We are not able to accomplish this.
We are implementing a KeycloakWebSecurityConfigurerAdapter and overriding the configure(HttpSecurity) method. Implementation looks like this:
super.configure(http);
http.csrf().disable()
.exceptionHandling()
.accessDeniedPage("/accessDenied");
http.anonymous.disable();
http.authorizeRequests();
With that code only, indeed every page is freely accessible, except the root page. As soon as we add calls to antMatcher() or anyRequest() method followed by permitAll() or fullyAuthenticated(), just to achieve the differentiation in allowed and disallowed pages, all pages are secured/disallowed. We played around a lot and tried to find help here and anywhere else but found no solution. Current implemented example is:
http.authorizeRequests().antMatchers(HttpMethod.GET, "/public/forgotPassword").permitAll()
.anyRequest().fullyAuthenticated();
The result is, as stated, that every pages needs authentication, also the public/forgotPassword page.
Does anyone have an idea about what the problem might be?
Thx in advance!
I've implemented this springboot.keycloak.mre1 to demonstrate — in a stripped-down way — how a previous project I worked on similarly implemented what I think you're requesting.
In a nutshell, the gist of the solution is…
…
public class SecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests().antMatchers("/login", "/login.html")
.permitAll().antMatchers("/dashboard", "/dashboard.html")
.authenticated();
}
…
}
The steps to build and run the MRE are straightforward. But if you get stuck building or running it, let me know if I can help you in any way.
And if I've completely misinterpreted what you've requested, then please feel free to clone and modify the project to be more like your use case. If you then upload your modifications, and elaborate on the specifics of your use case in the repo's Issues area, I will investigate and get back to you.
1 The MRE uses docker-compose because the original project it's based on did.
In my applications I am using the following config scheme:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.wissance.orgstructure.application.configuration;
import com.goodt.drive.goals.application.authentication.AppAuthenticationEntryPoint;
import com.goodt.drive.goals.application.services.users.KeyCloakUserInfoExtractorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.oauth2.resource.PrincipalExtractor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().sameOrigin(); // it is to fix issue with h2-console access
http.cors();
http.csrf().disable()
.authorizeRequests().antMatchers("/", "/callback", "/login**", "/webjars/**", "/error**").permitAll()
.and()
.authorizeRequests().antMatchers("/api/**").authenticated()
.and()
.authorizeRequests().antMatchers("/h2-console/**").permitAll()
.and()
.authorizeRequests().antMatchers("/swagger-ui.html").permitAll()
.and()
.authorizeRequests().antMatchers("/swagger-ui/**").permitAll()
.and()
.exceptionHandling().authenticationEntryPoint(new AppAuthenticationEntryPoint())
.and()
.logout().permitAll().logoutSuccessUrl("/");
}
#Bean
public PrincipalExtractor getPrincipalExtractor(){
return new KeyCloakUserInfoExtractorService();
}
#Autowired
private ResourceServerTokenServices resourceServerTokenServices;
}
#ControllerAdvice
public class AppAuthenticationEntryPoint implements AuthenticationEntryPoint{
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
// 401
logger.debug(String.format("Access to resource is denied (401) for request: \"%s\" message: \"%s\"", request.getRequestURL(), authException.getMessage()));
setResponseError(response, HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
}
#ExceptionHandler (value = {AccessDeniedException.class})
public void commence(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
// 403
logger.debug(String.format("Access to resource is forbidden (403) for request: \"%s\" message: \"%s\"", request.getRequestURL(), accessDeniedException.getMessage()));
setResponseError(response, HttpServletResponse.SC_FORBIDDEN, String.format("Access Denies: %s", accessDeniedException.getMessage()));
}
#ExceptionHandler (value = {NotFoundException.class})
public void commence(HttpServletRequest request, HttpServletResponse response, NotFoundException notFoundException) throws IOException {
// 404
logger.debug(String.format("Object was not found (404) for request: \"%s\" message: \"%s\"", request.getRequestURL(), notFoundException.getMessage()));
setResponseError(response, HttpServletResponse.SC_NOT_FOUND, String.format("Not found: %s", notFoundException.getMessage()));
}
#ExceptionHandler (value = {Exception.class})
public void commence(HttpServletRequest request, HttpServletResponse response, Exception exception) throws IOException {
logger.error(String.format("An error occurred during request: %s %s error message: %s",
request.getMethod(), request.getRequestURL(), exception.getMessage()));
// 500
setResponseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, String.format("Internal Server Error: %s", exception.getMessage()));
}
private void setResponseError(HttpServletResponse response, int errorCode, String errorMessage) throws IOException{
response.setStatus(errorCode);
response.getWriter().write(errorMessage);
response.getWriter().flush();
response.getWriter().close();
}
private final Logger logger = LoggerFactory.getLogger(this.getClass());
}
Config of spring security (application-local.yml) related to KeyCloak was listed below, in my app i have at least 3 different keycloak servers and i switch them from time to time, all my KeyCloak values passes from base settings (application.yml) currently using defined in appConfig.keyCloak.using as yml placeholder to selected keycloak? example of spring security config section:
security:
basic:
enabled: false
oauth2:
client:
clientId: ${appConfig.keyCloak.using.clientId}
clientSecret: ${appConfig.keyCloak.using.clientSecret}
accessTokenUri: ${appConfig.keyCloak.using.baseUrl}/protocol/openid-connect/token
userAuthorizationUri: ${appConfig.keyCloak.using.baseUrl}/protocol/openid-connect/auth
authorizedGrantTypes: code token
scope: local
username: ${appConfig.keyCloak.using.serviceUsername}
password: ${appConfig.keyCloak.using.servicePassword}
resource:
userInfoUri: ${appConfig.keyCloak.using.baseUrl}/protocol/openid-connect/userinfo
Example of one of KeyCloak server config:
baseUrl: http://99.220.112.131:8080/auth/realms/master
clientId: api-service-agent
clientSecret: f4901a37-efda-4110-9ba5-e3ff3b221abc
serviceUsername: api-service-agent
servicePassword: x34yui9034*&1
In my above example all pages that have the /api path in their url, i.e. /api/employee or /api/employee/find/? or others, are accessible only after authentication + authorization. All Swaggers pages or the login page are available without any authentication.
I am writing a spring boot application in which I am registering a URL to a bean via the SimpleUrlHandlerMapping configuration. Why am I not using the #Controller or #RequestMapping classes to do this ?!! Because I want to dynamically register URL's during runtime.
I am using the following code to register a simple URL to a controller
#Bean
public SimpleUrlHandlerMapping sampleServletMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Integer.MAX_VALUE - 2);
Properties urlProperties = new Properties();
urlProperties.put("/index", "myController");
mapping.setMappings(urlProperties);
return mapping;
}
The above code is working fine, I am able to hit the controller bean registered with the name "myController".
The issue appears when I use spring security. I introduced spring security and configured InMemoryAuthentication, and set my configuration as follows.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/index").permitAll()
.anyRequest()
.permitAll();
}
After doing this when I try to access /index path, it throws a 403, forbidden error. I have tried with permitAll() and fullyAuthenticated() configurations. It doesn't seem to work. However, any Controller class registered with the #Controller and #RequestMapping annotations are perfectly working fine with Security.
So, my assumption is that Spring Security is not aware of the dynamically registered URL's via the SimpleUrlHandlerMapping.
How do I solve this ? Is there a way I can tell spring security to include my dynamic URL registrations ? Unable to find any article on this online.
Suggestions and help much appreciated.
UPDATE:
Why csrf().disable() does works
CSRF stands for Cross Site Request Forgery
In simple words, it is one kind of token that is sent with the request to prevent the attacks. In order to use the Spring Security CSRF protection, we'll first need to make sure we use the proper HTTP methods for anything that modifies the state (PATCH, POST, PUT, and DELETE – not GET).
CSRF protection with Spring CookieCsrfTokenRepository works as follows:
The client makes a GET request to Server (Spring Boot Backend), e.g. request for the main page
Spring sends the response for GET request along with Set-cookie header which contains securely generated XSRF Token
The browser sets the cookie with XSRF Token
While sending a state-changing request (e.g. POST) the client (might be angular) copies the cookie value to the HTTP request header
The request is sent with both header and cookie (browser attaches the cookie automatically)
Spring compares the header and the cookie values, if they are the same the request is accepted, otherwise, 403 is returned to the client
The method withHttpOnlyFalse allows angular to read XSRF cookie. Make sure that Angular makes XHR request with withCreddentials flag set to true.
For more details, you may explore the following
Will Spring Security CSRF Token Repository Cookies Work for all Ajax Requests Automatically?
AJAX request with Spring Security gives 403 Forbidden
Basic CSRF Attack Simulation & Protection with Spring Security
Updated method configure(HttpSecurity http)
http
.csrf()
.ignoringAntMatchers("endpoint-to-be-ignored-for-csrf")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.authorizeRequests()
.antMatchers("/index").permitAll()
.anyRequest().authenticated();
Endpoint specified in antMatchers with permitAll() should not required authentication and antMatchers("/index").permitAll() should work fine.
Make sure your security configuration class is annotated with #EnableWebSecurity and #EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
The security configuration class is in follows the package structure and scanned by Spring. spring-component-scanning
You may find the minimal working example here
SecurityConfiguration.java
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// https://stackoverflow.com/a/56389047/10961238 -> WebSecurity vs HttpSecurity
// Add this method if .antMatchers("/index").permitAll() does not work
#Override
public void configure(WebSecurity web) throws Exception {
web.debug(true);
// web
// .ignoring()
// .antMatchers("/index");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/index").permitAll() //commenting this line will be results in 403
.anyRequest().authenticated();
}
}
SampleController.java
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#Controller("myController")
public class SampleController extends AbstractController {
#Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("::::::::::::::::::::::::::::::::Controller:::::::::::::::::::::::::::::::::");
response.getWriter().print("Hello world!");
return null;
}
}
MainApplication.java
import com.example.mappings.controller.SampleController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import java.util.Properties;
#SpringBootApplication
public class MappingsApplication {
public static void main(String[] args) {
SpringApplication.run(MappingsApplication.class, args);
}
#Bean
public SimpleUrlHandlerMapping sampleServletMapping() {
System.out.println("::::::::::::::::::::::::::::::::SimpleUrlHandlerMapping:::::::::::::::::::::::::::::::::");
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Integer.MAX_VALUE - 2);
Properties urlProperties = new Properties();
urlProperties.put("/index", sampleController());
mapping.setMappings(urlProperties);
return mapping;
}
#Bean
public SampleController sampleController() {
System.out.println("::::::::::::::::::::::::::::::::Setting SampleController:::::::::::::::::::::::::::::::::");
return new SampleController();
}
}
application.properties
spring.security.user.name = user
spring.security.user.password = user
spring.security.user.roles = ADMIN
I create applications - API client in Spring Boot with RestTemplate.
The API (not mine) probably has CORS, besouce in console I get 403, but in browser/postman is ok.
How to bypass CORS?
public Pokemontcg getPokemontcg() {
RestTemplate restTemplate = new RestTemplate();
Pokemontcg forObject = restTemplate.getForObject("https://api.pokemontcg.io/v1/cards?name=charizard", Pokemontcg.class);
return forObject;
}
Result
"main" org.springframework.web.client.HttpClientErrorException$Forbidden: 403 Forbidden
There are multiple ays. One of the most efficient is
doing it in JavaConfig
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
Or the simplest ways is
Controller Method CORS Configuration
Enabling CORS is straightforward – just add the annotation #CrossOrigin.
We may implement this in a number of different ways.
#CrossOrigin on a #RequestMapping-Annotated Handler Method
#RestController
#RequestMapping("/account")
public class AccountController {
#CrossOrigin
#RequestMapping("/{id}")
public Account retrieve(#PathVariable Long id) {
// ...
}
If you want client-side solution here is the solution
Consider this example.
Your server is my-server.com and your client is my-client.com Configure nginx as follows:
// nginx.conf
upstream server {
server my-server.com;
}
upstream client {
server my-client.com;
}
server {
listen 80;
server_name my-website.com;
access_log /path/to/access/log/access.log;
error_log /path/to/error/log/error.log;
location / {
proxy_pass http://client;
}
location ~ /server/(?<section>.*) {
rewrite ^/server/(.*)$ /$1 break;
proxy_pass http://server;
}
}
Here my-website.com will be the resultant name of the website where the code will be accessible (name of the proxy website). Once nginx is configured this way. You will need to modify the requests such that:
All API calls change from my-server.com/<API-path> to my-website.com/server/<API-path>
Oh, boi you have an absolutely different issue. It is not something bad with your code.
Read this thread https://github.com/simonprickett/allthepokemon/issues/1
Per this please change your endpoint to http://pokeapi.salestock.net/api/v2/
If this also doesn't help use https://cors.now.sh/https://your_URL.
https://cors.now.sh enables reverse proxy and with absolute URLs will definitely work.
That's quite a weird solution but it is what it is.
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
#Component
public class SimpleCORSFilter implements Filter {
private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class);
public SimpleCORSFilter() {
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, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me");
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void destroy() {
}
}
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.