Spring CORS settings - java

I'm trying to enable the cross origin header to be able to reach the service from anywhere (only on local env) but I cannot.
#Configuration
#RequiredArgsConstructor
public class CrossOriginConfig implements WebMvcConfigurer {
private final SecurityConfiguration securityConfiguration;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("*").allowedOrigins(securityConfiguration.getCrossOrginFilter());
}
}
I made a custom index.html with an ajax call and it fails due to the Allow-Cross-Origin header missing and it comes from another origin.
Simple Spring Boot 2.0 controllers are used with #RestController annotation and simple #GetMapping.
What I missed? What should I include and where?

You need to add the below annotation on either on the controller class or the specific method:
#CrossOrigin
By default, its allows all origins, all headers, the HTTP methods specified in the #RequestMapping annotation and a maxAge of 30 minutes is used.
If you want to allow only http://localhost:8080 to send cross-origin requests
#CrossOrigin(origins = "http://localhost:8080")
Replace the host and port accordingly.
Check the below Spring documentation for more information:
https://spring.io/guides/gs/rest-service-cors/

As Georg Wittberger pointed out the problem was with the mapping. I used the * wildcard what is not good for paths.
Instead of this: registry.addMapping("*")
I used this: registry.addMapping("/**") and it's working fine.

Related

Issue regarding JWT token and CORS in Spring and React

I am trying to create a spring boot application that uses a token authentication method. I wanted to go easy so I used this repo https://www.bezkoder.com/spring-boot-login-example-mysql/ as inspiration. No SQL problems. My code is exactly the same as that one there.
When I am doing requests in POSTMAN everything works fine and nothing is wrong.
When I am doing a request in the front end, I get a CORS error that I miss a header or some sort. I fixed that by adding the following class in the project
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowCredentials(true)
.allowedHeaders("*")
.allowedOrigins("http://localhost:3000");
}
}
At that point, I get the set-cookie header with the correct value, but the cookie is not set. I have also added the withCredentials: true in the header request in AXIOS. Can someone explain to me what is going on and show a solution to this problem using React as frontend?
Many thanks!

Spring Boot HTTP security configuration anonymous filter and a custom filter on a different path

I have experienced a strange problem while trying to configure HTTP security by using WebSecurityConfigurerAdapter. Here is the full configuration class I tried so far:
#Slf4j
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
#ConditionalOnProperty(name = "module.client.security.enabled")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${management.endpoints.web.base-path}")
private String managementEndpoint;
#Autowired
private List<ModuleProperties> moduleProperties;
#Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(this.managementEndpoint + "/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable();
http.formLogin().disable();
// FIXME also doesn't work because of the later http.requestMatchers().antMatchers() calls
http.authorizeRequests()
.antMatchers("/**").anonymous();
http.requestMatchers()
.antMatchers("/app/**")
.and()
.addFilterBefore(new ClientResolveFilter(), FilterSecurityInterceptor.class);
}
What I would like to do is actually enabling anonymous authentication for all endpoints — to prevent NullPointerExceptions when operating on SecurityContextHolder — Plus, enabling/adding a custom filter to only a subset or different endpoint path which is /app/** in this case.
I expected the above code would work but what exactly happens is the AnonymousAuthenticationFilter disabled for all and both filters only work for the path /app/** only.
If I remove the http.requestMatchers().antMatchers("/app/**") part, then AnonymousAuthenticationFilter works for all paths as normal. I suspect that the second .antMatchers("/app/**") call kinda replaces the former one or substitutes it implicitly which doesn't make sense to me, but I could be wrong.
I tried diving into the source but still confused and cannot find a clean solution to make it work as my expectation. Any ideas and help will be appreciated.
Cheers!
EDIT: I'm using Spring Boot 2.5.2 and the Spring Security version is 5.5.1
The addFilterBefore (and other addFilter*) method will add the filter to the filter chain which apply to all request. If you want the filter to apply to certain requests only, then you have to check inside the filter (eg. using HttpServletRequest.getgetRequestURI() to check the url).
After #Dickson's advice, I found a special bean called FilterRegistrationBean provided by spring boot.
Thus, I configured it as a bean which applies a specific servlet filter to only configured paths:
#Bean
public FilterRegistrationBean<ClientResolveFilter> clientResolveFilter(){
final FilterRegistrationBean<ClientResolveFilter> frb = new FilterRegistrationBean<>();
frb.setFilter(new ClientResolveFilter());
frb.addUrlPatterns("/app/*");
return frb;
}
This solution worked perfectly for my requirement.
Pay attention to that the path string is not an ant matcher now — must be written with single /app/* instead of double /app/** — it's actually the pattern when we manually configured web.xml file like in the old days :)

Set default response content type in Spring Boot REST API

Response content type on REST API endpoints (controller classes or methods) in Spring Boot can be set using the #Produces annotation. Is there a way to set this application wide as a default for every endpoint in the REST API? For example, instead of writing #Produces("application/json") on every controller class or endpoint, can this be set on the entry application class? Or is there any other way to configure a default used until explicitely overwritten?
If you want to set the default Accept header, not of default "Content-Type" header, so this solution will only impact responses, not requests.
As of Spring Boot 2.x, you need to create a class that extends the WebMvcConfigurer interface, e.g.:
#Configuration
class WebMvcConfiguration implements WebMvcConfigurer {
#Override
public void configureContentNegotiation( ContentNegotiationConfigurer configurer){
configurer.defaultContentType( MediaType.APPLICATION_JSON );
}
}
Let me know the result. Good luck.
Another option is to specify this default-media-type property in your application.properties:
spring.data.rest.default-media-type=application/json
Interestingly, it's also shown here as defaultMediaType. I believe you can use either the above or:
spring.data.rest.defaultMediaType=application/json
I'm not sure actually why there appear to be 2 ways to specify this same property.

Spring Boot CORS settings: CrossOrigin annotation works addCorsMappings doesn't

I have a Spring Boot Rest application where I need to allow CORS requests.
Requests use the common path <host>:<port>/api/... and so far I only have two resources:
package my.controllers;
#RestController
#RequestMapping(path="/", produces="application/json")
public class DataController {
#GetMapping("/")
public ApiResponse<Map<String, Object>> getValues() {
// ...
}
#GetMapping("/sub")
public ApiResponse<String> getValuesSub() {
// ...
}
Here are two example requests from the Javascript client running at http://localhost:3000/:
fetch('http://localhost:2001/api/').then(/* ... */);
fetch('http://localhost:2001/api/sub')).then(/* ... */);
If I add the #CrossOrigin(origins = "*") annotation to the controller, CORS requests work fine; if I replace it implementing WebMvcConfigurer.addCorsMappings() though:
#ComponentScan(basePackages={ "my.controllers" })
#Configuration
public class WebappConfig implements WebMvcConfigurer {
// ...
#Override
public void addCorsMappings(CorsRegistry registry) {
LogManager.getLogger(getClass()).info("CORS settings");
registry.addMapping("/api/**").allowedOrigins("*").maxAge(3600);
}
CORS requests fail, and I get (in Firefox, Chrome shows a similar error message):
CORS header 'Access-Control-Allow-Origin' missing
I see the log entry, so the method is surely invoked.
I've seen other questions mentioning Spring Security or Spring Data (I use none), so here's my dependencies list in case I'm missing something:
spring-boot-starter-web
spring-boot-starter-tomcat
spring-boot-starter-log4j2
spring-boot-configuration-processor
commons-lang3
commons-beanutils
What's the right way to set CORS settings to the whole application, without using the #CrossOrigin annotation on each controller?
UPDATE
I'm initializing a Rest servlet like this:
#Bean
public ServletRegistrationBean<DispatcherServlet> registerDispatcherServlet(DispatcherServlet servlet) {
ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(servlet);
servlet.setContextConfigLocation("");
registration.addUrlMappings("/api/*");
registration.setLoadOnStartup(1);
return registration;
}
The logs says:
Servlet dispatcherServlet mapped to [/api/*]
Servlet dispatcherServlet mapped to [/]
Servlet dispatcherServlet was not registered (possibly already registered?)
Could it be that the given Cors settings are going to the mentioned unregistered servlet?
I got it working by replacing "/api/**" with "/**" in the call to addMapping() when configuring the CORS registry:
#Override
public void addCorsMappings(CorsRegistry registry) {
LogManager.getLogger(getClass()).info("CORS things!");
registry.addMapping("/**").allowedOrigins("*").maxAge(3600);
}
Though I'm puzzled about why this is the setting that makes it work, and what would I have to do if I need to expose several independent Rest paths, i.e.:
http:/host:port/public-api (from all over the Internet)
http:/host:port/reserved-api (from well known hosts)
http:/host:port/admin-api (same host only)

Webflux Http 404 Response when header is added to Request

I am currently trying to shift my application from spring boot 1.5.x to 2.x.x on reactive stack. I am facing a kinda weird problem that I can't figure out. Hope someone knows the solution to this.
I implemented an api to receive a user jwt token as "Authorization" field on the header. The api is a POST method that receives a certain json data from the user in the body, goes to the backend and processes it.
The unfortunate thing is i keep getting a http 404 error when i add in the header, a normal 200 when i remove it in postman.
Here is my controller.
#RestController
#RequestMapping("/user")
#Slf4j
public class UserHandler {
#Autowired
private UserService service;
#Autowired
private Utility utility;
#PostMapping("/updateLink")
public Mono<ServerResponse> addNewAccountLinkAPI(#RequestHeader(name="Authorization") String id, #RequestBody UpdateAccountLink request){
return Mono.just(request)
.flatMap(s -> service.addNewAccountLink(s))
.flatMap(s -> ok().body(BodyInserters.fromObject(new RespWrap("Success", new Date(), null, s))))
.switchIfEmpty(badRequest().body(BodyInserters.fromObject(new RespWrap("Failed", new Date(), "Failed to create new link", null))));
}
}
Here is my simple security config
#Configuration
#EnableWebFluxSecurity
#EnableWebFlux
public class ResourceServerConfig implements WebFluxConfigurer {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, FirebaseAuthenticationManager manager) {
http
.authorizeExchange().anyExchange().permitAll()
.and().csrf().disable();
return http.build();
}
}
Can anyone please point me out on the problem. This sure seems like a lack of config problem.
I can see two issues with your code snippets.
First, you shouldn't add #EnableWebFlux as it completely disables the auto-configuration done by Spring Boot. Same goes for #EnableWebMvc in a Spring MVC application.
Second, you're mixing WebFlux annotations and WebFlux functional. The annotations you're using are fine, but the ServerResponse type should only be used when writing functional handlers. You should try instead here to use ResponseEntity.

Categories