In the previous post, we learned about the spring security login process. In this article, we will learn how to perform error handling and message localization with spring security login.
Message Localization with Spring Security
With login, it’s very important to provide the correct error message information to the user. For a simple application, we can work by providing some pre-defined error messages. For enterprise applications, we have to give the error messages in the user preferred language. We don’t want to show English message to a German customer.
In this article, we will look at the error handling and message localization with spring security login. Spring security by default will show login error in case customer provides invalid username or password. Spring security internally uses the Spring framework resource bundle feature to show customize error messages to the customer.
If you are starting, I highly recommend going through following articles to get basic understanding as how localization works in Spring MVC application.
- Internationalization in Spring Boot
- Custom Validation MessageSource in Spring Boot
- https://javadevjournal.com/spring-mvc/spring-bean-validation/
1. Defining Message Resources
Spring Boot application by default will look for internationalization key and values under /src/main/resources
folder. Let’s define two properties in the resource folder.
messages.properties
.messages_de.properties
Default locale file will name as messages.properties and files for other locales will have messages_xx.properties
a format where xx
is the locale code. Define the localized message as a key-value pair in these properties file. Here is the file from our code base.
lang.eng=English
lang.de= German
registration.validation.firstName=Please provide first name.
registration.validation.lastName=Please provide last name.
registration.validation.email=Please provide a valid email.
registration.validation.password= Password can not be empty.
login.error= Username or password is incorrect. Please provide valid username or password
German Language message file
lang.eng=Englisch
lang.de= Deutsche
registration.validation.firstName=Bitte geben Sie den Vornamen an
registration.validation.lastName=Bitte geben Sie den Nachnamen an
registration.validation.email=E-Mail darf nicht leer sein
registration.validation.password= Passwort kann nicht leer sein
login.error=Benutzername oder Passwort ist falsch. Bitte stellen Sie sicher, dass Sie einen gültigen Benutzernamen oder ein gültiges Passwort angeben.
I am also adding the properties for our registration process. In case your resource bundle location differs from what Spring Boot is expecting, please define the MessageSource
bean with location of the resource bundle.
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
The next step is to handle the locale and letting spring aware of the message property file.
2. Defining LocaleResolver
The LocaleResolver
helps in locale resolution strategies. Spring provides multiple options to handle the correct locale for the application.
- Request based locale handling.
- Session.
- Cookies
- Header.
By default, it uses the AcceptHeaderLocaleResolver
to get the locale based on the HTTP header. You can choose the implementation as per your need. For our spring security course, we are using session based strategy to get the locale.
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.ENGLISH);
return localeResolver;
}
We are setting default locale as English. As a next step, we need to identify in case customer changing or selecting their preferred language. This is done using the LocaleChangeInterceptor
.
3. LocaleChangeInterceptor
We need to configure an interceptor which allows for changing the current locale on every request.
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
return localeChangeInterceptor;
}
Keep in mind the following points:
- By default, the
LocalCangeInterceptor
use “locale
” as the parameter. In our case, we are using theparam
name as “lang
“. - Make sure you are passing the correct
param
to set the locale else you might get some unexpected results.
To complete the integration, we need to register our interceptor with Spring Boot. To register this bean with Spring Boot, we need to override addInterceptor()
method in our Configuration
class.
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
Optional Steps
The next 2 steps are optional and based on your application structure.
4. Defining LocalValidatorFactoryBean
If you are using JSR303 bean validation for your application and like to spring bean validation to pick the messages from the resource bundle, define LocalValidatorFactoryBean this in your configuration class.
@Bean
public LocalValidatorFactoryBean validator(MessageSource messageSource) {
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
bean.setValidationMessageSource(messageSource);
return bean;
}
End of Optional Section
Next step is to configure and handle the localization with spring security login.
5. Spring Security Login Error Configuration
The first step is to configure spring security. We need to tell spring security configuration as what to do in case there is any login error. We can do this using the failureURL
while configuring the login page.
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.....
.formLogin(form - > form
.loginPage("/login")
.defaultSuccessUrl("/home")
.failureUrl("/login?error=true")
);
}
In our case, we are sending the user back to the same login page but with an additional query parameter as “error=true
“. If you remember, we have created a login controller to display a custom login page with spring security and the same controller will be called when spring security throws a login error.We have multiple way to show the error message to the customer and here are few options:
- Show the message in case URL contains specific request parameter.
- Let our custom controller handle the error and work on customize error message.
5.1. Display localized error message using parameter
The quick and easy way is to display the message based on the request parameter. We can add a condition to show the error message only when there is a request parameter as “error
“. Here is the login page with this condition:
<form th:action="@{/login}" method="post">
<div th:if="${param.error}">
<div class="alert alert-danger">
<span th:text="#{login.error}"></span>
</div>
<!-- login section-->
</form>
When the error parameter is available in the request, we are displaying the localized error message for the login. Keep in mind that Spring will automatically determine the locale and pick the correct resource bundle to display the error message.
5.2. Error Message Localization using Controller
In case you want to have more control and like to perform some additional steps, you can handle it through the login controller. Collect the request parameter in your custom login controller and perform any additional logic.
@Controller
@RequestMapping("/login")
public class LoginPageController {
@GetMapping
public String login(@RequestParam(value = "error", defaultValue = "false") boolean loginError) {
if (loginError) {
// you custom error handling logic will go here
}
return "account/login";
}
}
To make sure our application is working as displaying the localized error messages with spring security login, let’s start and run our application. Once the application is up and running, open the http://localhost:8080/login
page and try with invalid credentials by selecting the distinct language.
Selecting Language as German.
Providing invalid credentials.
Changing language to English and trying with invalid credentials:
Summary
Handling localization with spring security is very important, and in this post we saw how to handle the message localization with Spring security login. The source code for this application is available on the GitHub.