Spring Boot login with a form

Previously I wrote about securing your application with social login. But not everybody has a social account. In this article we’re going to add formlogin to the application. Formlogin simply means that your users can log in with a username and password. We’re going to keep it as simple as possible, with in-memory authentication.

Adding users

Since we’re going to use usernames and passwords to allow users to login, we need to define a password encoder. Spring will not compare the literal password that it receives, but encodes it and then compare the encoded passwords. When the passwords are stored in the database, you don’t want to see them as plain text.

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

The next step is to add the users. We’re going to use inMemoryAuthentication with a single user, to keep it as simple as possible.

public class WebsiteApplication extends WebSecurityConfigurerAdapter {

	// some more code

	protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
				.withUser("user").password(passwordEncoder().encode("password")).roles("USER");
	}
}

Note that we’re using the passwordEncoder defined in the previous step to encode the password here. Also, we need to define a role, even though we’re not using it yet.

Allow formlogin

Now we need to tell Spring Security to allow the use of our login form.

public class WebsiteApplication extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		SimpleUrlAuthenticationFailureHandler handler = new SimpleUrlAuthenticationFailureHandler("/");
		// @formatter:off
		http
			.authorizeRequests(
					a -> a
				.antMatchers("/error", "/webjars/**","/oauth/**","/login").permitAll()
				.anyRequest().authenticated()
			)
			.formLogin(f -> f
					.loginPage("/login").permitAll()
			)
			// some more code
			;
		// @formatter:on
	}
	// some more code
}

Add login form

Now that we have enabled the formlogin in the backend, it’s time to add a login form in the frontend.

<form name='loginForm' th: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>

Webcontroller changes

There were two methods that use the authentication information. The first, index(), doesn’t need to change at all. Sure, we get a different Authentication implementation, but it still works unaltered.

@RequestMapping(value = {"/","/index"})
public String index(@CurrentSecurityContext(expression = "authentication") Authentication authentication) {
	if (authentication.isAuthenticated()) {
		return "redirect:/welcome";
	}
	return "redirect:/login";
}

The second method, where we try to find the username of the authenticated user does change. Here we drop the @CurrentSecurityContext annotation, and request the Principal directly. However, based on the method of authentication, we need to do something else to get the username. If the user used the login form, we can get the username directly from the principal. When the user authenticated using OAuth2, we need to dig a little deeper.

@RequestMapping(value = "/welcome")
public String welcome(Principal principal, Model model) {
	if (principal instanceof UsernamePasswordAuthenticationToken) {
		model.addAttribute("name", principal.getName());
	} else if (principal instanceof OAuth2AuthenticationToken){
		model.addAttribute("name", ((OAuth2AuthenticationToken) principal).getPrincipal().getAttribute("name"));
	}
	return "welcome";
}

Conclusion

Adding a login form to a Spring Boot application is easy. We’ve hardcoded a user with in memory authentication. Since we’re going to use a password, we need to have a password encoder. We’ve configured Spring Security to allow the user to login using a form. Then we’ve actually added a basic HTML login form, and as a last step we’ve modified the backend to work with a different type of principal.

Leave a Reply

Your email address will not be published. Required fields are marked *