gs-rest-service-cors
gs-rest-service-cors copied to clipboard
include spring security in example.
I'm about 10 billion kinds of frustrated with the documentation right now. Official and especially this guide. So let's start with this guide. It doesn't include how to make it work with spring security. I know spring security isn't required for CORS, but I think it's probably at least an 80% of the time use case. This still doesn't give me proper CORS headers.
I honestly think this is beyond a problem with just documentation though... but that's a good place to start.
// © Copyright 2024 Caleb Cushing
// SPDX-License-Identifier: AGPL-3.0-or-later
package com.xenoterracide.controller.authn;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository;
import org.springframework.boot.actuate.web.exchanges.InMemoryHttpExchangeRepository;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class ResourceServer {
ResourceServer() {}
public static void main(String[] args) {
SpringApplication.run(ResourceServer.class, args);
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.cors(Customizer.withDefaults())
.httpBasic(c -> c.disable())
.formLogin(f -> f.disable());
return http.build();
}
@Bean
HttpExchangeRepository httpExchangeRepository() {
return new InMemoryHttpExchangeRepository();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
var cors = new CorsConfiguration();
cors.addAllowedOrigin("http://localhost:3000");
cors.addAllowedMethod("*");
cors.addAllowedHeader("*");
var source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", cors);
return source;
}
@Bean
WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/external").allowedOrigins("http://localhost:3000");
}
};
}
@RestController
static class OidcTestController {
private final Logger log = LogManager.getLogger(this.getClass());
@CrossOrigin(originPatterns = "*")
@GetMapping("/api/external")
@NonNull
String index(@Nullable Authentication details) {
this.log.info("{}", details);
var name = details != null ? details.getName() : "world";
return "Hello, " + name;
}
}
}
* Host localhost:8080 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8080...
* Connected to localhost (::1) port 8080
> GET /api/external HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.6.0
> Accept: */*
>
< HTTP/1.1 200
< Vary: Origin
< Vary: Access-Control-Request-Method
< Vary: Access-Control-Request-Headers
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 12
< Date: Fri, 05 Apr 2024 10:13:21 GMT
<
{ [12 bytes data]
* Connection #0 to host localhost left intact
Hello, world
I found out that a big part of my problem is "testing" apparently there's a hidden undocumented optimization that prevents the headers from being written if the expected headers aren't in the request. So it would be good in addition to showing the expected spring security config to include how to test it within a junit test. That would show that you need headers.