Is there a way to disable the redirect for Spring Security and the login page. My requirements specify the login should be part of the navigation menu.
Example:
Therefore there is no dedicated login page. The login information needs to be submitted via Ajax. If an error occurs it should return JSON specifying the error and use the proper HTTP Status code. If authentication checks out it should return a 200 and then javascript can handle it from there.
I hope that makes sense unless there is any easier way to accomplish this with Spring Security. I don't have much experience with Spring Security. I assume this has to be a common practice, but I didn't find much.
Current spring security configuration
@Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/", "/public/**").permitAll() .antMatchers("/about").permitAll() .anyRequest().fullyAuthenticated() .and() .formLogin() .loginPage("/login") .failureUrl("/login?error") .usernameParameter("email") .permitAll() .and() .logout() .logoutUrl("/logout") .deleteCookies("remember-me") .logoutSuccessUrl("/") .permitAll() .and() .rememberMe(); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(userDetailsService) .passwordEncoder(new BCryptPasswordEncoder()); }
Update:
I tried using HttpBasic() but then it asks for login creds not matter what and its the ugly browser popup which is not acceptable to the end user. It looks like I may have to extend AuthenticationEntryPoint.
At the end of the day I need Spring security to send back JSON saying the authentication succeeded or failed.
In Spring Boot 2, if we want our own security configuration, we can simply add a custom WebSecurityConfigurerAdapter. This will disable the default auto-configuration and enable our custom security configuration.
The @EnableWebSecurity enables the web securities defined by WebSecurityConfigurerAdapter automatically. To override web securities defined by WebSecurityConfigurerAdapter in our Java configuration class, we need to extend this class and override its methods.
Spring security secures all HTTP endpoints by default. A user has to login in a default HTTP form. To enable Spring Boot security, we add spring-boot-starter-security to the dependencies.
The redirect behavior comes from SavedRequestAwareAuthenticationSuccessHandler which is the default success handler. Thus an easy solution to remove the redirect is to write your own success handler. E.g.
http.formLogin().successHandler(new AuthenticationSuccessHandler() { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { //do nothing } });
You need to disable redirection in a couple of different places. Here's a sample based on https://github.com/Apress/beg-spring-boot-2/blob/master/chapter-13/springboot-rest-api-security-demo/src/main/java/com/apress/demo/config/WebSecurityConfig.java
In my case, I don't return json body but only HTTP status to indicate success/failure. But you can further customize the handlers to build the body. I also kept CSRF protection on.
@Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired public void initialize(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception { // here you can customize queries when you already have credentials stored somewhere var usersQuery = "select username, password, 'true' from users where username = ?"; var rolesQuery = "select username, role from users where username = ?"; auth.jdbcAuthentication() .dataSource(dataSource) .usersByUsernameQuery(usersQuery) .authoritiesByUsernameQuery(rolesQuery) ; } @Override protected void configure(HttpSecurity http) throws Exception { http // all URLs are protected, except 'POST /login' so anonymous user can authenticate .authorizeRequests() .antMatchers(HttpMethod.POST, "/login").permitAll() .anyRequest().authenticated() // 401-UNAUTHORIZED when anonymous user tries to access protected URLs .and() .exceptionHandling() .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) // standard login form that sends 204-NO_CONTENT when login is OK and 401-UNAUTHORIZED when login fails .and() .formLogin() .successHandler((req, res, auth) -> res.setStatus(HttpStatus.NO_CONTENT.value())) .failureHandler(new SimpleUrlAuthenticationFailureHandler()) // standard logout that sends 204-NO_CONTENT when logout is OK .and() .logout() .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler(HttpStatus.NO_CONTENT)) // add CSRF protection to all URLs .and() .csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) ; } }
Here's a deep explanation of the whole process, including CSRF and why you need a session: https://spring.io/guides/tutorials/spring-security-and-angular-js/
Scenarios that I tested:
happy path GET /users/current (or any of your protected URLs) request --> no cookie <- response 401 + cookie XSRF-TOKEN POST /login -> header X-XSRF-TOKEN + cookie XSRF-TOKEN + body form with valid username/password <- 204 + cookie JSESSIONID GET /users/current -> cookie JSESSIONID <- 200 + body with user details POST /logout -> header X-XSRF-TOKEN + cookie XSRF-TOKEN + cookie JSESSIONID <- 204 === exceptional #1: bad credentials POST /login -> header X-XSRF-TOKEN + cookie XSRF-TOKEN + body form with bad username/password <- 401 === exceptional #2: no CSRF at /login (like a malicious request) POST /login -> cookie XSRF-TOKEN + body form with valid username/password <- 401 (I would expect 403, but this should be fine) === exceptional #3: no CSRF at /logout (like a malicious request) (user is authenticated) POST /logout -> cookie XSRF-TOKEN + cookie JSESSIONID + empty body <- 403 (user is still authenticated)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With