Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems using Spring login in REST with CORS

I am trying to implement a website using REST. My strategy to authenticate the users consist of sending a JWT token to the user in reply to a username/password combination sent via POST. The most relevant part of my security conf is shown below.

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/images/**", "/scripts/**", "/styles/**", "favicon.ico");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
                .disable()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .exceptionHandling()
                .authenticationEntryPoint(new RESTAuthenticationEntryPoint()).and()
            .formLogin()
                .successHandler(authenticationSuccessHandler())
                .failureHandler(new SimpleUrlAuthenticationFailureHandler())
                .loginProcessingUrl("/login") //Not necesary because is the default
                .permitAll().and()
            .authorizeRequests()
                .antMatchers("/api/getStatistics").permitAll()
                .anyRequest().denyAll().and()
            .addFilterBefore(new JwtTokenAuthenticationFilter(jWTTokenService()), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public SavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler() {
        return new RESTAuthenticationSuccessHandler(jWTTokenService());
    }

    @Bean
    public JWTTokenService jWTTokenService() {
        return new JWTTokenServiceImpl();
    }

To allow the CORS access I have written the following lines in a class extending of WebMvcConfigurerAdapter

@Override
public void addCorsMappings(CorsRegistry registry){
    registry.addMapping("/api/**")
                .allowedOrigins("*")
                .allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept")
                .allowedMethods("GET", "POST", "OPTIONS")
                .allowCredentials(true).maxAge(3600);
    registry.addMapping("/login")
                .allowedOrigins("*")
                .allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept")
                .allowedMethods("POST", "OPTIONS")
                .allowCredentials(true).maxAge(3600);
}

So when I make a call to /login sending the username and password it is supposed that Spring will catch the request, will process it and then will redirect to the success or failure handler.

Well, instead of that I have gotten an 403 Forbidden response during the CORS preflight. I decide to debug the program because I thought that when I wrote formLogin(), the UsernamePasswordAuthenticationFilter create a new AntPathRequestMatcher with the value ("/login", "POST").

What I found in the debug console was the following Request 'OPTIONS /login' doesn't match 'POST /login

Of course it does not! Some hours later trying to solve the problem I discovered that everything works if I declare a empty method /login because during the preflight Spring finds the method and then send a 200OK to the client so the client then is allowed to send a POST that is captured by the UsernamePasswordAuthenticationFilter.

@Controller
public class LoginController {

    @RequestMapping(value = { "/login" }, method = RequestMethod.POST)
    public void dummyLogin() {

    }
}

So, my question is: Should I really declare an empty method to "cheat" during the CORS preflight or it is just that I have missed something? Because it is not so elegant to declare a dummy method when you really want to delegate the job to the UsernamePasswordAuthenticationFilter...

like image 599
IErreBe Avatar asked Feb 22 '26 18:02

IErreBe


1 Answers

The problem is that org.springframework.security.web.authentication.logout.LogoutFilter and org.springframework.security.web.authenticationUsernamePasswordAuthenticationFilter do not continue with the filter chain if they handled a login/logout. And since the configuration via WebSecurityConfigurerAdapter is processed later in the chain, the CorsProcessor is never applied.

I decided to keep the old solution and use a org.springframework.web.filter.CorsFilter.

like image 77
sematnic Avatar answered Feb 24 '26 06:02

sematnic