5.3.2 Creating a custom login page
The default login page is much better than the clunky HTTP basic dialog box you started with, but it’s still rather plain and doesn’t quite fit with the look of the rest of the Taco Cloud application.
To replace the built-in login page, you first need to tell Spring Security what path your custom login page will be at. That can be done by calling formLogin() on the HttpSecurity object, as shown next:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests()
.antMatchers("/design", "/orders").access("hasRole('USER')")
.antMatchers("/", "/**").access("permitAll()")
.and()
.formLogin()
.loginPage("/login")
.and()
.build();
}Notice that before you call formLogin(), you bridge this section of configuration and the previous section with a call to and(). The and() method signifies that you’re finished with the authorization configuration and are ready to apply some additional HTTP configuration. You’ll use and() several times as you begin new sections of configuration.
After the bridge, you call formLogin() to start configuring your custom login form. The call to loginPage() after that designates the path where your custom login page will be provided. When Spring Security determines that the user is unauthenticated and needs to log in, it will redirect them to this path.
Now you need to provide a controller that handles requests at that path. Because your login page will be fairly simple—nothing but a view—it’s easy enough to declare it as a view controller in WebConfig. The following addViewControllers() method sets up the login page view controller alongside the view controller that maps “/” to the home controller:
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
registry.addViewController("/login");
}Finally, you need to define the login page view itself. Because you’re using Thymeleaf as your template engine, the following Thymeleaf template should do fine:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Taco Cloud</title>
</head>
<body>
<h1>Login</h1>
<img th:src="@{/images/TacoCloud.png}"/>
<div th:if="${error}">
Unable to login. Check your username and password.
</div>
<p>New here? Click
<a th:href="@{/register}">here</a> to register.</p>
<form method="POST" th:action="@{/login}" id="loginForm">
<label for="username">Username: </label>
<input type="text" name="username" id="username" /><br/>
<label for="password">Password: </label>
<input type="password" name="password" id="password" /><br/>
<input type="submit" value="Login"/>
</form>
</body>
</html>The key things to note about this login page are the path it posts to and the names of the username and password fields. By default, Spring Security listens for login requests at /login and expects that the username and password fields be named username and password. This is configurable, however. For example, the following configuration customizes the path and field names:
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/authenticate")
.usernameParameter("user")
.passwordParameter("pwd")Here, you specify that Spring Security should listen for requests to /authenticate to handle login submissions. Also, the username and password fields should now be named user and pwd.
By default, a successful login will take the user directly to the page that they were navigating to when Spring Security determined that they needed to log in. If the user were to directly navigate to the login page, a successful login would take them to the root path (for example, the home page). But you can change that by specifying a default success page, as shown next:
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/design")As configured here, if the user were to successfully log in after directly going to the login page, they would be directed to the /design page.
Optionally, you can force the user to the design page after login, even if they were navigating elsewhere prior to logging in, by passing true as a second parameter to defaultSuccessUrl as follows:
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/design", true)Signing in with a username and password is the most common way to authenticate in a web application. But let’s have a look at another way to authenticate a user that uses someone else’s login page.
