Spring Security Form Login

1. Introduction

This article is going to focus on Login with Spring Security. We’re going to build on top of the simple previous Spring MVC example, as that’s a necessary part of setting up the web application along with the login mechanism.

Further reading:

Spring Security – Redirect to the Previous URL After Login

A short example of redirection after login in Spring Security

Read more

Two Login Pages with Spring Security

A quick and practical guide to configuring Spring Security with two separate login pages.

Read more

Spring Security Form Login

A Spring Login Example – How to Set Up a simple Login Form, a Basic Security XML Configuration and some more Advanced Configuration Techniques.

Read more

2. The Maven Dependencies

To add Maven dependencies to the project, please see the Spring Security with Maven article. Both standard spring-security-web and spring-security-config will be required.

3. Spring Security Java Configuration

Let’s start by creating a Spring Security configuration class that extends WebSecurityConfigurerAdapter. By adding @EnableWebSecurity, we get Spring Security and MVC integration support:

@Configuration
@EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
          .withUser("user1").password(passwordEncoder().encode("user1Pass")).roles("USER")
          .and()
          .withUser("user2").password(passwordEncoder().encode("user2Pass")).roles("USER")
          .and()
          .withUser("admin").password(passwordEncoder().encode("adminPass")).roles("ADMIN");
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        http
          .csrf().disable()
          .authorizeRequests()
          .antMatchers("/admin/**").hasRole("ADMIN")
          .antMatchers("/anonymous*").anonymous()
          .antMatchers("/login*").permitAll()
          .anyRequest().authenticated()
          .and()
          .formLogin()
          .loginPage("/login.html")
          .loginProcessingUrl("/perform_login")
          .defaultSuccessUrl("/homepage.html", true)
          //.failureUrl("/login.html?error=true")
          .failureHandler(authenticationFailureHandler())
          .and()
          .logout()
          .logoutUrl("/perform_logout")
          .deleteCookies("JSESSIONID")
          .logoutSuccessHandler(logoutSuccessHandler());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

In this example, we’ve used in-memory authentication and defined 3 users.

Let’s now go through the elements we’ve used to create the form login configuration.

3.1. authorizeRequests()

We’re allowing anonymous access on /login so that users can authenticate. We’re also securing everything else.

Note that the order of the antMatchers() elements is significant – the more specific rules need to come first, followed by the more general ones.

3.2. formLogin()

This has several methods that we can use to configure the behavior of the form login:

  • loginPage() – the custom login page

  • loginProcessingUrl() – the url to submit the username and password to

  • defaultSuccessUrl() – the landing page after a successful login

  • failureUrl() – the landing page after an unsuccessful login

3.3. Authentication Manager

The Authentication Provider is backed by a simple, in-memory implementation – InMemoryUserDetailsManager specifically. This is useful for rapid prototyping when a full persistence mechanism is not yet necessary.

Starting with Spring 5, we also have to define a password encoder. In our example, we’ve used the BCryptPasswordEncoder.

4. Add Spring Security to the Web Application

To use the above-defined Spring Security configuration, we need to attach it to the web application. In this case, we don’t need any web.xml:

public class AppInitializer
  extends AbstractAnnotationConfigDispatcherServletInitializer {

    protected Class<?>[] getRootConfigClasses() {
        return new Class[] {SecSecurityConfig.class};
    }
}

Note that this initializer is not necessary if we’re using a Spring Boot application. Have a look at our article on Spring Boot security auto-configuration for more details on how the security configuration is loaded in Spring Boot.

5. The Spring Security XML Configuration

Let’s also have a look at the corresponding XML configuration.

The overall project is using Java configuration, so we need to import the XML configuration file via a Java @Configuration class:

@Configuration
@ImportResource({ "classpath:webSecurityConfig.xml" })
public class SecSecurityConfig {
   public SecSecurityConfig() {
      super();
   }
}

And the Spring Security XML Configuration – webSecurityConfig.xml:

<http use-expressions="true">
    <intercept-url pattern="/login*" access="isAnonymous()" />
    <intercept-url pattern="/**" access="isAuthenticated()"/>

    <form-login login-page='/login.html'
      default-target-url="/homepage.html"
      authentication-failure-url="/login.html?error=true" />
    <logout logout-success-url="/login.html" />
</http>

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="user1" password="user1Pass" authorities="ROLE_USER" />
        </user-service>
        <password-encoder ref="encoder" />
    </authentication-provider>
</authentication-manager>

<beans:bean id="encoder"
  class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
</beans:bean>

6. The web.xml

Before the introduction of Spring 4, we used to configure Spring Security configuration in the web.xml – only an additional filter added to the standard Spring MVC web.xml:

<display-name>Spring Secured Application</display-name>

<!-- Spring MVC -->
<!-- ... -->

<!-- Spring Security -->
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

The filter – DelegatingFilterProxy – simply delegates to a Spring-managed bean – the FilterChainProxy – which itself is able to benefit from full Spring bean lifecycle management and such.

7. The Login Form

The login form page is going to be registered with Spring MVC using the straightforward mechanism to map views names to URLs with no need for an explicit controller in between:

registry.addViewController("/login.html");

This, of course, corresponds to the login.jsp:

<html>
<head></head>
<body>
   <h1>Login</h1>
   <form name='f' action="login" method='POST'>
      <table>
         <tr>
            <td>User:</td>
            <td><input type='text' name='username' value=''></td>
         </tr>
         <tr>
            <td>Password:</td>
            <td><input type='password' name='password' /></td>
         </tr>
         <tr>
            <td><input name="submit" type="submit" value="submit" /></td>
         </tr>
      </table>
  </form>
</body>
</html>

The Spring Login form has the following relevant artifacts:

  • login – the URL where the form is POSTed to trigger the authentication process

  • username – the username

  • password – the password

8. Further Configuring Spring Login

We briefly discussed a few configurations of the login mechanism when we introduced the Spring Security Configuration above – let’s go into some detail now.

One reason to override most of the defaults in Spring Security is to hide the fact that the application is secured with Spring Security and minimize the information a potential attacker knows about the application.

Fully configured, the login element looks like this:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.formLogin()
      .loginPage("/login.html")
      .loginProcessingUrl("/perform_login")
      .defaultSuccessUrl("/homepage.html",true)
      .failureUrl("/login.html?error=true")
}

Or the corresponding XML configuration:

<form-login
  login-page='/login.html'
  login-processing-url="/perform_login"
  default-target-url="/homepage.html"
  authentication-failure-url="/login.html?error=true"
  always-use-default-target="true"/>

8.1. The Login Page

Next, let’s see how we can configure a custom login page using the loginPage() method:

http.formLogin()
  .loginPage("/login.html")

Or, via XML configuration:

login-page='/login.html'

If we don’t specify this, Spring Security will generate a very basic Login Form at the /login URL.

8.2. The POST URL for Login

The default URL where the Spring Login will POST to trigger the authentication process is /login which used to be /j_spring_security_check before Spring Security 4.

We can use the loginProcessingUrl method to override this URL:

http.formLogin()
  .loginProcessingUrl("/perform_login")

Or, via XML configuration:

login-processing-url="/perform_login"

A good reason to override this default URL is to hide the fact that the application is actually secured with Spring Security – that information should not be available externally.

8.3. The Landing Page on Success

After a successful Login process, the user is redirected to a page – which by default is the root of the web application.

We can override this via the defaultSuccessUrl() method:

http.formLogin()
  .defaultSuccessUrl("/homepage.html")

Or with XML configuration:

default-target-url="/homepage.html"

In case the always-use-default-target is set to true, then the user is always redirected to this page. If that attribute is set to false, then the user will be redirected to the previous page they wanted to visit before being prompted to authenticate.

8.4. The Landing Page on Failure

Same as with the Login Page, the Login Failure Page is autogenerated by Spring Security at /login?error by default.

To override this, we can use the failureUrl() method:

http.formLogin()
  .failureUrl("/login.html?error=true")

Or with XML:

authentication-failure-url="/login.html?error=true"

9. Conclusion

In this Spring Login Example, we configured a simple authentication process – we discussed the Spring Security Login Form, the Security Configuration and some of the more advanced customizations available.

The implementation of this Spring Login tutorial can be found in the GitHub project – this is an Eclipse based project, so it should be easy to import and run as it is.

When the project runs locally, the sample HTML can be accessed at:

http://localhost:8080/spring-security-mvc-login/login.html

Leave a Reply

Your email address will not be published.