In the previous episode we learned how to load user details from a database. Now it’s on time to replace the default login form that comes with Spring Security with our own.

Project Setup

If you are new to this series on Spring Security and WebFlux, check my initial blog post to see how to start from scratch.

Today’s Takeaway

  • Create a custom login page with additional features
  • Understand how login and logout work

A complete example is available on GitLab.

Follow the video tutorial below for a step-by-step guidance.

The Default Login Form

The form looks okay, but it lacks some essential features:

  • What if you have forgotten your password?
  • How about new user’s registration?
  • What if you don’t want to create an account at all and use your Google or Facebook login instead?

By the way, if you are interested how the default login works under the hood, watch my explanation in this video.

The Fully Customized Login Form

Credit: W3Schools

This is what we end up with at the end of this post in 3 easy steps:

  1. Add a new html template
  2. Add a new stylesheet
  3. Tweak login and logout

Tired of reading? Watch detailed instructions with explanations and additional hints!

Step 1: Add a New HTML Template

Create a new file login.html under src/main/resources/templates and paste the html code from W3Schools in it. Here is the result:

Step 2: Add a New Stylesheet

Create a new file main.css under src/main/resources/static and the css code from W3Schools in it. Here is the source code.

Step 3: Tweak Login and Logout

For the custom login page to work you need to explicitly add a route to it in the security config, like this:

@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity):   SecurityWebFilterChain =
        http
            .authorizeExchange()
            .pathMatchers("/login", "/*.css").permitAll()
            .anyExchange().authenticated()
            .and().authenticationManager(authenticationManager())
            .formLogin().loginPage("/login")
            .and().logout()
            .requiresLogout(ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/logout")).and().build()

See the full source code for additional clarity.

Isn’t it crazy that even though the route /login is identical with the default route it still needs to be explicitly declared in the configuration? Watch my explanation to learn more about it.

Additionally, we mustn’t forget to add a custom handler, that will actually route to our new login form! Remember, once you start making changes you are on your own.

The custom handler of /login:

Another super important observation is the fact that the login route has to be whitelisted as a publicly accessible resource!:

http.pathMatchers("/login", "/*.css").permitAll()

If you forget to add the config above, you will end up with “too many redirects” error in the browser, as I explain in this part of the video.

Summary

In this post I showed you in a few simple steps how to add a custom login form. When it comes to styling / design there are many resources available for free. Always remember to add an attribution link as a thank you.

Fork the complete example on GitLab and make it your own.

Watch this episode on YouTube, give it a like, subscribe to the channel and stay tuned for my next episode where we finally delve into interesting stuff! I will show you how to add a password-less social login via Google.


Tomas Zezula

Hello! I'm a technology enthusiast with a knack for solving problems and a passion for making complex concepts accessible. My journey spans across software development, project management, and technical writing. I specialise in transforming rough sketches of ideas to fully launched products, all the while breaking down complex processes into understandable language. I believe a well-designed software development process is key to driving business growth. My focus as a leader and technical writer aims to bridge the tech-business divide, ensuring that intricate concepts are available and understandable to all. As a consultant, I'm eager to bring my versatile skills and extensive experience to help businesses navigate their software integration needs. Whether you're seeking bespoke software solutions, well-coordinated product launches, or easily digestible tech content, I'm here to make it happen. Ready to turn your vision into reality? Let's connect and explore the possibilities together.