user15006167
user15006167

Reputation: 234

Spring Security ignoring context-path

Controller

@GetMapping("/api/data")
String response(){

}

application.properties

server.servlet.context-path=/v1

Spring secuity

http.authorizeRequests().anyMatcher("/v1/**").authenicated()

Here authentication is not happening. I believe, spring-security is ignoring the context-path thats been configured in the application.properties. Why is spring-security ignoring the context path. How to fix this?

For the above image, I expected a 401 since the v1/** is supposed to be authorised

This is working fine,

http.authorizeRequests().anyMatcher("/**").authenicated()

Upvotes: 2

Views: 2969

Answers (2)

mindOf_L
mindOf_L

Reputation: 940

In my case, I'm working with Spring Security 6 + Spring Boot 3. In the application.yml I set this configuration to set the root path for the API:

spring.mvc.servlet.path=/api/v1

I have a very simple class to check CORS policy before starting to work with anything else in the API, which is:

@Controller
public class AliveController {

    @GetMapping("/alive")
    public ResponseEntity<Object> getAlive() {
        return ResponseEntity.status(200).body("Everything OK!");
    }
}

And in my class SecurityConfiguration, which is annotated with @Configuration and @EnableWebSecurity, I put these two methods annotated with @Bean:

@Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .csrf().disable()
                .cors(Customizer.withDefaults())
                .headers().frameOptions().disable().and()
                .authorizeHttpRequests()
                .requestMatchers("/alive").permitAll()
                .anyRequest().authenticated().and()
                .securityMatcher("/**")
                .build();

    }

Note that I put this /alive endpoint to bypass authentication requirement, also I referenced it without root path (/api/v1). Also, note that I have in the cors method a parameter Customizer.withDefaults(), that is invoking the default method to configure cors named in Spring by default as corsConfigurationSource(). So, Spring will search for a method with that name and I named that way. Then, I set my own ruled cors in that method in the same class, something like:

@Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        // corsOrigins, allowedHeaders, allowedMethods, exposedHeaders
        // are String arrays set as private final in the class
        config.setAllowedOrigins(List.of(corsOrigins));
        config.setAllowedHeaders(List.of(allowedHeaders));
        config.setAllowedMethods(List.of(allowedMethods));
        config.setExposedHeaders(List.of(exposedHeaders));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }

Also, I'm noticed something about paths (something that's got me all over the place). If you set in the spring.mvc.servlet.path property the value /api/v1, cors policy supposes you'll set that policy within /api/v1, so you don't need to set again in the rules in securityMatcher("/**") or in source.registerCorsConfiguration("/**", config);. The policy will get those /** considering there's a root preceding /api/v1.

Trying in the terminal with cors origin http://localhost:3000...

~ http get :8080/api/alive Origin:http://localhost:3000
HTTP/1.1 200
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Expose-Headers: Allow, X-Get-Header
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: keep-alive
Content-Length: 14
Content-Type: text/plain;charset=UTF-8
Date: Sat, 11 Mar 2023 08:07:11 GMT
Expires: 0
Keep-Alive: timeout=60
Pragma: no-cache
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 0

Response:

Everything OK!

Trying with another port...

~ http get :8080/api/alive Origin:http://localhost:3001
HTTP/1.1 403
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: keep-alive
Date: Sat, 11 Mar 2023 08:09:14 GMT
Expires: 0
Keep-Alive: timeout=60
Pragma: no-cache
Transfer-Encoding: chunked
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-XSS-Protection: 0

Response:

Invalid CORS request

Please, be aware of the List.of(corsOrigins), List.of(allowedHeaders), List.of(allowedMethods) and List.of(exposedHeaders), which are Lists coming from something like:

private final String corsOrigins = "http://localhost:3000";

private final String[] allowedMethods = {"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"};

private final String[] allowedHeaders = {"Authorization", "Accept", "Accept-Language", "content-type", "Requestor-Type", "X-Requested-With"};

private final String[] exposedHeaders = {"Allow", "X-Get-Header"};

It's been hours of my life I won't get back with this thing hehe.

Hope it helps.

Upvotes: 1

ima.technophyle
ima.technophyle

Reputation: 667

Turn ON the debug for Spring Security, then you would understand what's happening.

@EnableWebSecurity(debug = true)

When,

server.servlet.context-path=/v1

Generated Request:

Request received for GET '/api/data':

servletPath:/api/data
pathInfo:null

When,

spring.mvc.servlet.path=/v1

Generated Request:

Request received for GET '/v1/api/data':

servletPath:/v1
pathInfo:/api/data

Go with servlet-path for what you are trying to implement...

Upvotes: 6

Related Questions