How to handle CORS URLs on Prod/Dev environments? - spring

In our Spring Boot app, we made the first deployment on our Quality environment and now we want to make it simple defining URLs to accept petitions from our FrontEnd application.
We build our application with maven and then we execute it with the command
java -Dspring.profiles.active=prod -jar myapp-0.0.1-SNAPSHOT.jar
We thought we could set the URL on the application.properties/application-prod.properties file, but this does not work as in execution time it is null. Another workaround would be somehow to get the parameter -Dspring.profiles.active=prod we pass when running the application and then take one URL or another but this seems to be a little dirty...
So what do you guys would do? I was impressed not finding anything on google, apparently people have different workarounds or I am searching in the wrong way.
Edit
Cross Origin info:
This is how we implemented it at first.
#CrossOrigin(origins = BasicConfiguration.CLIENT_URL)
And this is how we want to do it now with a filter with Spring Security
public class CorsFilter implements Filter, ApplicationContextAware {
#Value("${urlServer}")
private String urlServer;
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", urlServer);
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
response.setHeader("Access-Control-Expose-Headers", "Location");
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig filterConfig) {}
#Override
public void destroy() {}
}
Of course urlServer is defined in application.properties with its corresponding metadata.
EDIT 2
How I initialize the filter:
#Bean
public FilterRegistrationBean corsFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CorsFilter());
registration.addUrlPatterns("/sessionLogin");
return registration;
}

The problem is that you CorsFilter is not a spring bean. You can eather define it like a bean, or do something like this:
#Bean
public FilterRegistrationBean corsFilter(#Value("${app.cors.url.server}") String urlServer) {
FilterRegistrationBean registration = new FilterRegistrationBean();
CorsFilter corsFilter = new CorsFilter();
corsFilter.setUrlServer(urlServer);
registration.setFilter(corsFilter);
registration.addUrlPatterns("/sessionLogin");
return registration;
}
Of course, you will need to define setter in your CorsFilter for urlServer field

Related

How to add CORS to outh2/resource server in Spring Boot 2.x?

I have an oauth server and a resource server that I have created with JWT.
I also created an angular front end with 2 buttons:
The first button calls the auth server and gets the JWT token and adds it to the input box.
The second button calls the rest server with the JWT token as a bearer Authorisation http header.
Calling the 2 services from PostMan works perfectly but I cannot get the CORS setup correctly configured for the back end services.
Both buttons are giving me the below error:
Access to XMLHttpRequest at 'http://localhost:8085/oauth/token' from origin 'http://localhost:4200' has been blocked by CORS policy: Request header field authorization is not allowed by Access-Control-Allow-Headers in preflight response.
I added all 3 of these projects to my public github repo.
I have tried to add CORS with several ways:
The config on the resource rest service is smaller so I will outline that here
I tried adding the default .cors() on the HttpSecurity as well as setting it manually in the corsConfigurationSource() method.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable()
.cors()
.and().authorizeRequests().anyRequest().authenticated()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
//I tried manually configured the cors as well
/*http.csrf().disable()
.cors().configurationSource(corsConfigurationSource())
.and().authorizeRequests().anyRequest().authenticated();
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);*/
}
/* #Bean
CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST"));
configuration.setAllowCredentials(true);
//the below three lines will add the relevant CORS response headers
configuration.addAllowedOrigin("*");
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
*/
}
I also tried adding a servlet filter
#Component #Order(Ordered.HIGHEST_PRECEDENCE) public class
SimpleCorsFilter implements Filter {
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws
IOException, ServletException {
final HttpServletResponse response = (HttpServletResponse) servletResponse;
final HttpServletRequest request = (HttpServletRequest) servletRequest;
response.addHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "content-type, x-requested-with, authorisation");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
#Override
public void destroy() {
} }
Just can't get it to work. Can anyone please give me some guidelines here?
Silly mistake on my end as in both my SimpleCorsFilter.java files I specified that authorisation header tags are allowed but it is not authorisation with an S but with a Z.
Changing both the files in my config server
response.setHeader("Access-Control-Allow-Headers", "content-type,
x-requested-with, Authorization");
Extends your class with withWebMvcConfigurer rather than WebSecurityConfigurerAdapter. The override the following method:
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE").allowedHeaders("*");
}
It should add the origin. You can play with '*' and make many combination. I have given you idean now it is your turn to play with this API.

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.

How do I enable CORS headers in the Swagger /v2/api-docs offered by Springfox Swagger?

I have the following file in my project:
#Configuration
#Order(Ordered.LOWEST_PRECEDENCE)
public class SwaggerConfig {
#Bean
public Docket apiSwagger2Documentation() { .... }
}
And in the Application.java there is:
#SpringBootApplication
#ComponentScan(basePackages = { ... })
#EnableSwagger2
public class Application {
...
}
The Swagger JSON is available under /v2/api-docs, that works fine.
What I would like to do, is to enable CORS headers for that endpoint.
For my own controllers, I have added #CrossOrigin to the controller classes, those APIs then have CORS headers, that works fine. But for the Swagger JSON URL I haven't written a controller myself, so I cannot use that annotation.
I have added the following method to the SwaggerConfig, as described in "Global CORS Configuration" in CORS support in Spring Framework.
#Bean
public WebMvcConfigurer corsConfigurer() {
System.out.println("*** corsConfigurer called");
return new WebMvcConfigurerAdapter() {
#Override public void addCorsMappings(CorsRegistry registry) {
System.out.println("*** addCorsMappings called");
registry.addMapping("/v2/api-docs");
}
};
}
Both print statements get printed, so the method is being called. But when I call the URL with curl:
curl -H "Origin: foo.com" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS \
--verbose \
http://localhost:9274/v2/api-docs
The CORS headers are not in the response. (In contrast to my own controller methods, annotated with #CrossOrigin, where the response does have the CORS headers.)
I am using springfox-swagger2 version 2.7.0, and spring-boot-starter-web 1.5.2.
What can I do to enable CORS headers on the Swagger JSON API endpoint?
I think you need a generic web filter as opposed to Web Mvc configuration.
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// Allow anyone and anything access. Probably ok for Swagger spec
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/v2/api-docs", config);
return new CorsFilter(source);
}
Thanks to #Barath for the answer. The solution was to ignore the Spring documentation, that code just seems to silently not work.
(It's a shame, the Spring stuff is quite advanced when it does work, for example, the "Access-Control-Allow-Headers" response header to the pre-flight request is set based on what headers the Java API method actually offers.)
Ignore Spring's implementation of CORS and do your own. I have put the code here that worked for me:
#Component
public class CorsFilter 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", request.getHeader("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", "Foo, Bar, Baz");
chain.doFilter(req, res);
}
#Override
public void init(FilterConfig filterConfig) {}
#Override
public void destroy() {}
}
Remember to add any #RequestHeader that you've used in any REST method to the Access-Control-Allow-Headers response header

CORS issue with Spring Boot

I have a Spring Boot application running on port 8443, and an angular2 based front end on port 8080. I need my front end to make requests to my Spring server, but I'm getting CORS errors left and right. I have added the #CrossOrigin annotation to my RestController method, and I have added a CORSFilter to my project, and mapped it on web.xml, but on Firefox 46.0a2 I still get this error on the console:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at https://localhost:8443/allEquips. (Reason: CORS
header 'Access-Control-Allow-Origin' missing).
The relevant part of my controller:
#CrossOrigin
#RequestMapping("/allequips")
List<String> allequips(Model model) {
List<String> codes = equipmentRepository.findAllEquipments();
return codes;
}
The CORSFilter:
public class CORSFilter 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, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
The mapping on web.xml:
<filter>
<filter-name>cors</filter-name>
<filter-class>config.CORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And I don't know if this is important, but the Angular2 code that's making the http request:
#Injectable()
export class EquipService {
equips: Array<Equip>;
constructor(public http: Http) {
console.log('Equip service created.', http);
}
getEquips() {
return this.http.get(WebServiceEndPoint+'allEquips')
.map((responseData) => {
return responseData.json();
}).map((equips: Array<any>) => {
let result: Array<Equip> = [];
if(equips) {
equips.forEach((equip) => {
result.push(new Equip(equip.code));
});
}
return result;
}).subscribe( res => this.equips = res);
}
}
Am I missing some configuration? Is my code wrong in any way?
EDIT: I gave up and restarted from a previous commit. After that, simply adding #Cross-Origin was enough.
First Approach:-
If you are using spring boot then create a new class that extends WebMvcConfigurerAdapter
#Configuration
#ComponentScan
#EnableWebMvc
public class ApplicationConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
// Can just allow `methods` that you need.
registry.addMapping("/**").allowedMethods("PUT", "GET", "DELETE", "OPTIONS", "PATCH", "POST");
}
}
Second Approach:-
Also you can add this in the #SpringBootApplication annotated class. No xml needed.
origin, headers, methods etc are all configurable based on your needs.
#Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*"); // this allows all origin
config.addAllowedHeader("*"); // this allows all headers
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
I'm pretty sure you need to add Content-Type in the allowed headers
response.setHeader("Access-Control-Allow-Headers", "x-requested-with x-uw-act-as");
Here's what I have working in my project:
#Component
public class CrossOriginRequestFilter implements Filter {
//Configurable origin for CORS - default: * (all)
#Value("${app.http.filter.cors.origin:*}")
private String originList;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)req;
HttpServletResponse httpResponse = (HttpServletResponse) res;
String origin = httpRequest.getHeader("Origin");
if (origin == null) {
//this is the case of mobile, where it sends null as Origin
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
} else if (origin != null && originList.contains(origin)) {
httpResponse.setHeader("Access-Control-Allow-Origin", origin);
} else {
httpResponse.setHeader("Access-Control-Allow-Origin", "https://yourdomain.com");
}
httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Headers", "Accept, Accept-CH, Accept-Charset, Accept-Datetime, Accept-Encoding, Accept-Ext, Accept-Features, Accept-Language, Accept-Params, Accept-Ranges, Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methods, Access-Control-Allow-Origin, Access-Control-Expose-Headers, Access-Control-Max-Age, Access-Control-Request-Headers, Access-Control-Request-Method, Age, Allow, Alternates, Authentication-Info, Authorization, C-Ext, C-Man, C-Opt, C-PEP, C-PEP-Info, CONNECT, Cache-Control, Compliance, Connection, Content-Base, Content-Disposition, Content-Encoding, Content-ID, Content-Language, Content-Length, Content-Location, Content-MD5, Content-Range, Content-Script-Type, Content-Security-Policy, Content-Style-Type, Content-Transfer-Encoding, Content-Type, Content-Version, Cookie, Cost, DAV, DELETE, DNT, DPR, Date, Default-Style, Delta-Base, Depth, Derived-From, Destination, Differential-ID, Digest, ETag, Expect, Expires, Ext, From, GET, GetProfile, HEAD, HTTP-date, Host, IM, If, If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, Keep-Alive, Label, Last-Event-ID, Last-Modified, Link, Location, Lock-Token, MIME-Version, Man, Max-Forwards, Media-Range, Message-ID, Meter, Negotiate, Non-Compliance, OPTION, OPTIONS, OWS, Opt, Optional, Ordering-Type, Origin, Overwrite, P3P, PEP, PICS-Label, POST, PUT, Pep-Info, Permanent, Position, Pragma, ProfileObject, Protocol, Protocol-Query, Protocol-Request, Proxy-Authenticate, Proxy-Authentication-Info, Proxy-Authorization, Proxy-Features, Proxy-Instruction, Public, RWS, Range, Referer, Refresh, Resolution-Hint, Resolver-Location, Retry-After, Safe, Sec-Websocket-Extensions, Sec-Websocket-Key, Sec-Websocket-Origin, Sec-Websocket-Protocol, Sec-Websocket-Version, Security-Scheme, Server, Set-Cookie, Set-Cookie2, SetProfile, SoapAction, Status, Status-URI, Strict-Transport-Security, SubOK, Subst, Surrogate-Capability, Surrogate-Control, TCN, TE, TRACE, Timeout, Title, Trailer, Transfer-Encoding, UA-Color, UA-Media, UA-Pixels, UA-Resolution, UA-Windowpixels, URI, Upgrade, User-Agent, Variant-Vary, Vary, Version, Via, Viewport-Width, WWW-Authenticate, Want-Digest, Warning, Width, X-Content-Duration, X-Content-Security-Policy, X-Content-Type-Options, X-CustomHeader, X-DNSPrefetch-Control, X-Forwarded-For, X-Forwarded-Port, X-Forwarded-Proto, X-Frame-Options, X-Modified, X-OTHER, X-PING, X-PINGOTHER, X-Powered-By, X-Requested-With");
chain.doFilter(req, httpResponse);
}
#Override
public void destroy() {
}
}
Here originList is a list of origins you want to allow, configured from application.yml or properties file.

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.

Resources