问题
I have a spring boot app which provides HTML page service via / and also rest api via /api. The former requires login via a Login form and the later requires HTTP basic auth, and hereby, I configure two HttpSecurity section as follows:
@Configuration
@Order(1)
public static class ApiSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.cors().and().csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic(Customizer.withDefaults());
}
}
@Configuration
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.cors().disable()
.authorizeRequests()
.antMatchers("/login", "/js/**", "/css/**").permitAll()
.antMatchers("/**").hasRole("ADMIN")
.and().formLogin().loginPage("/login").permitAll().defaultSuccessUrl("/index")
.and().logout().invalidateHttpSession(true)
.deleteCookies("JSESSIONID").logoutSuccessUrl("/login").permitAll();
}
}
The configuration works perfectly in normal case. However, if wrong credentials are provides in rest api clients, an HTML code of login page with HTTP status code 200 returns from the app instead of an excepted json message with HTTP status code 401 or 403.
I'm afraid it is because the url pattern of /api/** both matches "/api/**" and "/**", and therefore, the request will pass both the filter chain for rest api and HTML page. Finally, because of the lower order of formLogin, a login page returns in the case.
So, How can I get the excepted result for rest api clients? Is there an only solution to separate the two url patterns which should not match each other?
Addition 1:
I think there are three cases which will raise exceptions in the security filter chain:
- No credentials provided;
- Wrong credentials provided;
- Right credentials provided but not matched the required roles
And the results for the cases are as follows:
- Return HTTP status 401 with a json error message
- Return HTTP status 302 and try to redirect to login page
- Return HTTP status 403 with a json error message
Therefore, it seems that only the case of wrong credentials provided will
be routed to /error endpoint (as what Eleftheria said in the answer), and the difference between 1,3 and 2 is the exception type -- org.springframework.security.access.AccessDeniedException: Access is denied for 1 and 3; org.springframework.security.authentication.BadCredentialsException: Bad credentials for 2.
In the formLogin() case, if the BadCredentialsException rises, it will be routed to the failure-url, but how to configure the failure-url in the httpbasic case? (seems no such method in HttpBasicConfigurer)
回答1:
This is happening because the failed authentication is throwing an exception and the "/error" page is secured by your second filter chain.
Try permitting all requests to "/error" in your second filter chain.
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/error").permitAll()
// ....
Note that the request will only be processed by one filter chain, and that is the first filter chain that it matches.
The request to "/api/123" is only processed by the first filter chain, however the second filter chain was invoked because there was an error.
来源:https://stackoverflow.com/questions/64134630/how-to-return-http-status-code-instead-of-login-page-in-multi-httpsecurity-case