Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security Disable Login Page / Redirect

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:

enter image description here

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.

like image 727
greyfox Avatar asked Jul 30 '15 03:07

greyfox


People also ask

How do I disable basic security in spring boot?

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.

What does EnableWebSecurity annotation do?

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.

Does Spring Security use default login form?

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.


2 Answers

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     } }); 
like image 129
ben Avatar answered Sep 21 '22 15:09

ben


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) 
like image 42
André Avatar answered Sep 20 '22 15:09

André