Spring Boot CORS enable through configuration - spring-boot

I've read many posts regarding CORS in Spring (Boot) but none could answer my question, but maybe I just missed the answer, so bear with me.
I have a REST Webservice currently used only for server to server calls. I now want to open some endpoints to be called directly from the browser, but not from the same domain, thus CORS. I got it working for some endpoints by doing two things:
1. enabling OPTIONS in my WebSecurityConfigurerAdapter:
http.authorizeRequests()
.mvcMatchers(HttpMethod.OPTIONS,
"/endpont1",
"/endpoint2")
.permitAll()
2. adding the following annotation to my #GetMapping for these endpoints:
#CrossOrigin(origins = "${cors.origin}", allowCredentials = "true",
exposedHeaders = ResponseUtils.CONTENT_DISPOSITION)
#GetMapping("/endpoint1")
The problem is, as far as I understand the documentation, leaving origins empty allows CORS for any domain. And I don't want to allow OPTIONS if I don't need CORS.
What is the best way to make this configurable through a properties file?
The "embedded" application.properties should have it disabled, but if the tenant wants to enable it we can provide an additional application-tenant.properties where we could enable it for certain domains and start the application with the appropriate profile.
EDIT: I found an answer in another post which looks interesting and maybe I can do this conditionally:
https://stackoverflow.com/a/43559288/3737177

After a few try and errors I found a working solution based on this answer:
#Configuration
#EnableConfigurationProperties
#Order(1)
public class EndpointSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private RequestMatcher requestMatcher;
#Value("${cors.origins:}")
private String corsOrigins;
#Override
protected void configure(HttpSecurity http) throws Exception {
if (StringUtils.isNotBlank(corsOrigins)) {
http.cors().configurationSource(buildConfigurationSource());
}
http.requestMatchers().mvcMatchers("/endpoint1", "/pendpoint2")
.and().csrf().requireCsrfProtectionMatcher(requestMatcher)
.and().authorizeRequests().anyRequest()
.hasAnyRole(SecurityConfiguration.ROLE_ENDPOINT_USER, SecurityConfiguration.ROLE_ADMIN)
.and().httpBasic();
}
private CorsConfigurationSource buildConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList(corsOrigins.split(",")));
configuration.setAllowedMethods(Arrays.asList("GET");
configuration.setAllowedHeaders(Arrays.asList("authorization"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/endpoint1", configuration);
source.registerCorsConfiguration("/endpoint2", configuration);
return source;
}
}
If there is a cors.origins property in the application-tenant.properties, it enables CORS and configures the allowed methods and headers. CSRF is also enabled for same origin requests.

The truth is that you CANNOT set the global CORS configuration using the application.properties file. You HAVE TO use JavaConfig as described here.
implements WebMvcConfigurer
and override below method
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain4.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(false).maxAge(4200);
}
Or
Add below code snippet in Application.java
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
}
};
}
I think this way you can add properties in a property file and use those in code here and based on different flavors you can override those properties.

For loading custom properties files you can use
spring.config.import=optional:classpath:cors.yml
or from java args
-Dspring.config.import=optional:classpath:cors.yml
This method support reading from file, from url, repository and config server
Spring documentation about this
For reading CORS configuration from properties file you may use library (I'm developer of this)
<dependency>
<groupId>io.github.iruzhnikov</groupId>
<artifactId>spring-webmvc-cors-properties-autoconfigure</artifactId>
<version>VERSION</version>
</dependency>
and properties config
spring:
web:
cors:
enabled: true
mappings: #spring.web.cors.mappings.<any_name>.<property>: <value>
anyName: #just any name, just for grouping properties under the same path pattern (not used in internal logic)
paths: #ant style path pattern, ATTENTION! not ordered, /** pattern override all other pattern
- /path/to/api
- /path/to/api/**
#allowed-origins: "*"
allowed-methods: GET #Enable override all defaults! If disabled: a lot more from all the controller methods included from the path pattern matches
#allowed-headers: "*"
#exposed-headers: ('*' - not-supported)
#allow-credentials: true
allowed-origin-patterns: .*
#max-age: PT30M

Related

How to get Application properties configuration into Annotation as parameter

I want to use #CrossOrigin annotation on my RestController in my Spring Boot application and set origins parameter with the values from application.properties file.
#CrossOrigin(origins = {"${app.cors.origins}"})
public class SomeController(){
//
//
}
I set the property in my application.properties file like
app.cors.origins =http://www.google.com,http://localhost:8001
However that doesn't work as a cross origin request from http://localhost:8001 to my app fails with CORS error on the browser.
Am I missing something on setting the property?
Update : Problem is to set origins as a string array from the value of application property entry. When I hardcode the urls in origin, it works.
Thanks
If you want to use application.properties to set origins of CORS then this is the solution.
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Value("${app.cors.origins}")
private String corsAllowedOrigins;
#Value("${app.cors.methods}")
private String corsAllowedMethods;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(corsAllowedOrigins)
.allowedMethods(corsAllowedMethods);
}
application.properties
app.cors.origins=http://www.google.com,http://localhost:8001
app.cors.methods=GET,OPTIONS
I'm also working on this issue, I found another post mentioned about controller level parameter annotation for Cors. I haven't try it yet. share it with you:
Create your custom annotation and annotate the API with that.
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
#CrossOrigin
public #interface CrossOriginsList {
public String[] crossOrigins() default {
"http://domain1.com", "http://domain1.com"
"http://domain1.com", "http://domain1.com"
// Pass as many as you want
};
}
And now Annotate your API with this custom Annotation
#CrossOriginsList
public String methodName() throws Exception
{
//Business Logic
}

How to add a custom AuthenticationDetailsSource to BearerTokenAuthenticationFilter?

The BearerTokenAuthenticationFilter uses an AuthenticationDetailsSource to build the details of an authentication request:
authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
I am implicitly using the OAuth2ResourceServerConfigurer, provided by spring-security-config-5.7.2, which sadly doesn't consider a developer-defined AuthenticationDetailsSource:
BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
filter.setBearerTokenResolver(bearerTokenResolver);
filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);
filter = postProcess(filter);
http.addFilter(filter);
I confirm that the BearerTokenAuthenticationFilter has the setter I need:
setAuthenticationDetailsSource()
But I am unable to find a proper and simple way of using the setter (or any other way) to use a custom AuthenticationDetailsSource for that specific filter. I am trying to avoid creating a new filter or a new configuration.
What I have tried:
Went to github to see if there are any new versions - there are none unfortunately.
Tried to autowire the spring security filter chain and directly set the AuthenticationDetailsSource for the filter, but with no success so far.
Is there someone who managed to easily set the AuthenticationDetailsSource for a BearerTokenAuthenticationFilter?
Later edit
I have posted this question as a github issue for the Spring Security team:
https://github.com/spring-projects/spring-security/issues/11655
According to jzheaux#GitHub and as pointed in the accepted answer, I successfully used an ObjectPostProcessor:
http
.oauth2ResourceServer((oauth2) -> oauth2
.jwt(withDefaults())
.withObjectPostProcessor(new ObjectPostProcessor<BearerTokenAuthenticationFilter>() {
#Override
public BearerTokenAuthenticationFilter postProcess(BearerTokenAuthenticationFilter object) {
object.setAuthenticationDetailsSource(myAuthenticationDetailsSource);
return object;
}
});
To set your own AuthenticationDetailsSource, create ObjectPostProcessor class, where you can use setAuthenticationDetailsSource:
public class MyObjectPostProcessor implements ObjectPostProcessor<BearerTokenAuthenticationFilter> {
#Override
public <O extends BearerTokenAuthenticationFilter> O postProcess(O filter) {
filter.setAuthenticationDetailsSource(new MyAuthenticationDetailsSource());
return filter;
}
}
Then you can set MyObjectPostProcessor when creating SecurityFilterChain configuration:
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.oauth2ResourceServer()
.withObjectPostProcessor(new MyObjectPostProcessor());
return http.build();
}
}

Apache Shiro is breaking the CORS configuration of my Spring Boot RestAPI

After tinkering with different security frameworks, I've decided to go with Apache Shiro for my Spring Boot RestAPI, because it appears to offer the necessary flexibility without too much bureaucratic overhead. So far, I haven't done anything except adding the maven dependency to my project:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.5.1</version>
</dependency>
This forced me to define a Realm bean in order to get the application started:
#Bean
public Realm realm() {
return new TMTRealm();
}
The bean pretty much does nothing yet, except for implementing the Realm interface:
public class TMTRealm implements Realm {
private static final String Realm_Name = "realm_name";
#Override
public String getName() {
return Realm_Name;
}
#Override
public boolean supports(AuthenticationToken token) {
return false;
}
#Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
return null;
}
}
So far so good. Except that now my RestAPI is violating CORS policy by not adding the 'Access-Control-Allow-Origin' header to any of its responses. I've noticed that Chrome doesn't send any dedicated OPTIONS request but two requests of the same method, GET in this case, with the first one failing as follows:
Access to XMLHttpRequest at 'http://localhost:8081/geo/country/names?typed=D&lang=en-US' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Without Shiro present it works perfectly fine, both the elegant way of using Spring's #CrossOrigin annotation on the controller and the brute force 'old school' way of defining a CorsFilter bean:
#Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("OPTIONS", "GET", "POST", "PUT", "DELETE"));
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
I've implemented Spring's ApplicationListener interface to hook into when the ApplicationContext is started and can thus see that the corsFilter bean is registered and present:
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("############ - application context started with beans: ");
final String[] beans = event.getApplicationContext().getBeanDefinitionNames();
Arrays.parallelSort(beans);
for (final String bean : beans) {
System.out.println(bean);
}
}
Output:
...
conditionEvaluationDeltaLoggingListener
conventionErrorViewResolver
corsFilter
countryController
...
But the filter is never called upon any request (I've set a breakpoint and System.out to prove it). I've also noticed that there are three Shiro beans present:
shiroEventBusAwareBeanPostProcessor
shiroFilterChainDefinition
shiroFilterFactoryBean
Therefore, I assume that probably the shiroFilterFactoryBean is breaking it somehow and needs extra attention and configuration. Unfortunately the Apache Shiro documentation doesn't seem to say anything about cross-origin requests, and I would assume that this is not (necessarily) part of Shiro's security concerns but rather of the underlying Restful API, that is Spring. Googling the issue didn't yield any helpful results either, so my suspicion is that I'm missing something big, or worse, something small and obvious here. While I'm trying to figure this out, any help or hint is greatly appreciated, thanks!
Awight, I figured it out. It's been a while that I was in filter-servlet land, so I didn't think of the order in which filters are executed. The naive way that I did it, the Shiro filter chain was always executed before my custom CorsFilter (and apparently the default Spring processor of #CrossOrigin annotation as well). Since I haven't yet configured Shiro yet, any request would be rejected as neither authenticated nor authorized, and so the CorsFilter was never executed causing a response without Access-Control-Allow-Origin header.
So, either I configure Shiro properly or just make sure to have the CorsFilter executed prior to the Shiro filter by using Spring's FilterRegistrationBean like this (setOrder to zero):
#Configuration
public class RestApiConfig {
#Bean
public FilterRegistrationBean<CorsFilter> corsFilterRegistrationBean() {
final FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>(this.corsFilter());
registration.setOrder(0);
return registration;
}
private CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOrigins(Arrays.asList("*"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowedMethods(Arrays.asList("OPTIONS", "GET", "POST", "PUT", "DELETE"));
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
#Bean
public Realm realm() {
return new TMTRealm();
}
}
Edit:
D'uh, it was actually too easy for me to notice. All you have to do is to configure a ShiroFilterChainDefinition bean, so that it will refer to annotations on the controller classes or methods, like this:
#Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
final DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
chainDefinition.addPathDefinition("/**", "anon");
return chainDefinition;
}
Just like it is described in the documentation. Now it works both with a CorsFilter bean or with Spring's #CrossOrigin annotation. If there is no Shiro annotation present on the controller method, the request will be passed through.

Enable CORS origin graphql

I'm working on graphQL and spring boot project. The API works well using graphiQL but when trying to consume it using Apollo vueJS, it causes CORS origin error.
I'm using #CrossOrigin annotation in ProductQuery class which implements GraphQLQueryResolver like below:
#CrossOrigin(origins = "https://localhost:8081")
public List<Product> getProducts(){return this.productService.findAll(); }
Here is the error displayed on frontEnd project:
I appreciate your help.
For local development you may need a CorsFilter bean to enable your local origin:
#Configuration
#Profile("local")
public class LocalCorsConfiguration {
#Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("http://localhost:3000");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/graphql/**", config);
return new CorsFilter(source);
}
}
Don't forget to start the application with -Dspring.profiles.active=local.
To solve this issue you need to add this in your application properties graphql.servlet.corsEnabled: true after that your server response header will have the CORS properties.
What worked for me was the solution explained in the official docs
My version of a configurer bean looks like this:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(final CorsRegistry registry) {
registry.addMapping("/graphql/**")
.allowedOrigins(CorsConfiguration.ALL)
.allowedHeaders(CorsConfiguration.ALL)
.allowedMethods(CorsConfiguration.ALL);
}
};
}
Since Spring Boot 2.7.0 there are configuration properties for CORS with GraphQL:
spring:
graphql:
cors:
allow-credentials: true
allowed-origins:
- http://localhost:3000
See GraphQlCorsProperties.java for further properties.

Simple Reverse Proxy with Spring Boot and Netflix Zuul

I'm looking to implement a simple reverse proxy with Spring Boot that is:
Easy to add routes
Ability to add custom authentication on a per route basis
Add additional headers as needed
I've looked at the facilities provided by the #EnableZuulProxy annotation but it seems too heavyweight as I don't have a desire to use Eureka, Ribbon, or Hystrix. However, #EnableZuulServer is a bit light on configuration.
Would anyone be able to provide an example of what I'm after? Is Netflix Zuul the right choice for this or is there another library I should be looking at?
Thanks!
Simple Reverse Proxy Server
It's easy to set up a simple proxy reverse using Spring Boot without Ribbon, Eureka, or Hystrix.
Simply annotate your main application class with #EnableZuulProxy and set the following property in your configuration:
ribbon.eureka.enabled=false
Then define your routes in your configuration like such:
zuul.routes.<route_name>.path=<route_path>
zuul.routes.<route_name>.url=http://<url_to_host>/
where <route_name> is an arbitrary name for your route and <route_path> is a path using Ant-style path matching.
So a concrete example would be something like this
zuul.routes.userservice.path=users/**
zuul.routes.userservice.url=http://localhost:9999/
Custom Filters
You can also implement your custom authentication and any additional headers by extending and implementing the ZuulFilter class and adding it as an #Bean to your #Configuration class.
So another concrete example:
public class MyFilter extends ZuulFilter {
#Override
public String filterType() {
// can be pre, route, post, and error
return "pre";
}
#Override
public int filterOrder() {
return 0;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
// RequestContext is shared by all ZuulFilters
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
// add custom headers
ctx.addZuulRequestHeader("x-custom-header", "foobar");
// additional custom logic goes here
// return isn't used in current impl, null is fine
return null;
}
}
and then
#Configuration
public class GatewayApplication {
#Bean
public MyFilter myFilter() {
return new myFilter();
}
}
Zuul is a good choice. Am not sure about other alternatives but, we've started building Zuul filters (Pre/Post and Route) that could intercept the request and do all pre/post processing and route based upon your need. It is not mandatory to use the whole bunch of Eureka, Ribbon and Hysterix along with Zuul.

Resources