Customizing login
We've seen how Spring Security makes it very easy to get started. Now let's see how we can customize the login experience. In the following code snippet, we demonstrate the usage of some of the more common ways to customize login, but we encourage you to refer to the reference documentation of Spring Security, which includes an Appendix, Additional Reference Material with all of the supported attributes.
Let's take a look at the following steps to customize login:
- First, update your SecurityConfig.java file as follows:
//src/main/java/com/packtpub/springsecurity/configuration/
SecurityConfig.java
http.authorizeRequests()
...
.formLogin()
.loginPage("/login/form")
.loginProcessingUrl("/login")
.failureUrl("/login/form?error")
.usernameParameter("username")
.passwordParameter("password")
....
Let's take a look at the following methods depicted in the preceding code snippet:
- The loginPage() method specifies where Spring Security will redirect the browser if a protected page is accessed and the user is not authenticated. If a login page is not specified, Spring Security will redirect the user to /spring_security_login. Then o.s.s.web.filter.FilterChainProxy will choose o.s.s.web.authentication.ui.DefaultLoginPageGeneratingFilter, which renders the default login page as one of the delegates, since DefaultLoginPageGeneratingFilter is configured to process /spring_security_login by default. Since we have chosen to override the default URL, we are in charge of rendering the login page when the /login/form URL is requested.
- The loginProcessingUrl() method defaults to /j_spring_security_check and specifies the URL that the login form (which should include the username and password) should be submitted to, using an HTTP post. When Spring Security processes this request, it will attempt to authenticate the user.
- The failureUrl() method specifies the page that Spring Security will redirect to if the username and password submitted to loginProcessingUrl() are invalid.
- The usernameParameter() and passwordParameter() methods default to j_username and j_password respectively, and specify the HTTP parameters that Spring Security will use to authenticate the user when processing the loginProcessingUrl() method.
It may be obvious, but if we only wanted to add a custom login page, we would only need to specify the loginPage() method. We would then create our login form using the default values for the remaining attributes. However, it is often good practice to override the values of anything visible to users, to prevent exposing that we are using Spring Security. Revealing what frameworks we are using is a type of information leakage, making it easier for attackers to determine potential holes in our security.
- The next step is to create a login page. We can use any technology we want to render the login page, as long as the login form produces the HTTP request that we specified with our Spring Security configuration when submitted. By ensuring the HTTP request conforms to our configuration, Spring Security can authenticate the request for us. Create the login.html file, as shown in the following code snippet:
//src/main/webapp/WEB-INF/tempates/login.html
<p class="container">
<!--/*/ <th:block th:include="fragments/header :: header">
</th:block> /*/-->
<form th:action="@{/login}" method="POST"
cssClass="form-horizontal">
<p th:if="${param.error != null}" class="alert
alert-danger">
<strong>Failed to login.</strong>
<span th:if="${session[SPRING_SECURITY_LAST_EXCEPTION]
!= null}">
<span th:text="${session
[SPRING_SECURITY_LAST_EXCEPTION].message}">
Invalid credentials</span>
</span>
</p>
<p th:if="${param.logout != null}"
class="alert alert-success">You have been logged out.
</p>
<label for="username">Username</label>
<input type="text" id="username" name="username"
autofocus="autofocus"/>
<label for="password">Password</label>
<input type="password" id="password" name="password"/>
<p class="form-actions">
<input id="submit" class="btn" name="submit"
type="submit"
value="Login"/>
</p>
</form>
</p>
Remember that if you are having problems typing anything in the book, you can refer to the solution at the next checkpoint (chapter02.02-calendar).
The following number of items are worth highlighting in the preceding login.html file:
- The form action should be /login, to match the value provided for the loginProcessingUrl() method we specified. For security reasons, Spring Security only attempts to authenticate when using POST by default.
- We can use param.error to see whether there was a problem logging in, since the value of our failureUrl() method, /login/form?error, contains the HTTP parameter error.
- The session attribute, SPRING_SECURITY_LAST_EXCEPTION, contains the last o.s.s.core.AuthenticationException exception, which can be used to display the reason for a failed login. The error messages can be customized by leveraging Spring's internationalization support.
- The input names for the username and password inputs are chosen to correspond to the values we specified for the usernameParameter() and passwordParameter() methods in our SecurityConfig.java configuration.
- The last step is to make Spring MVC aware of our new URL. This can be done by adding the following method to WebMvcConfig:
//src/main/java/com/packtpub/springsecurity/web/configuration/
WebMvcConfig.java
import org.springframework.web.servlet.config.annotation.
ViewControllerRegistry;
...
public class WebMvcConfig extends WebMvcConfigurationSupport {
public void addViewControllers(ViewControllerRegistry
registry){
registry.addViewController("/login/form")
.setViewName("login");
}
...
}