I have a spring application that I want users to be able to change the preferred locale. Currently users can change the locale for the current session but I want to be able to save the users option so that whenever they log in, the saved locale is used if one exists. I have a mysql database which I'm using to store the user locale preference. I have created a custom AuthenticationSuccessHandler to handle changing the locale to the saved locale, which works for a user where I have already saved the locale to the database. However, the bit I don't know how to do is save the locale when the option is changed. The code is as follows:
/**
* Creates and gets the LocaleResolver
* @return LocaleResolver
*/
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
slr.setDefaultLocale(Locale.getDefault());
logger.debug("Setting locale to: " + Locale.getDefault());
return slr;
}
/**
* Creates and gets the LocaleChangeInterceptor
* @return LocaleChangeInterceptor
*/
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
lci.setParamName("lang");
logger.debug("localeChangeInterceptor called " + Locale.getDefault());
return lci;
}
SecurityConfig class:
@Configuration("SecurityConfig")
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
class SecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
@Qualifier("CustomAuthenticationSuccessHandler")
private CustomAuthenticationSuccessHandler authenticationSuccessHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding(StandardCharsets.UTF_8.name());
filter.setForceEncoding(true);
http.addFilterBefore(filter,CsrfFilter.class);
// configure authentication providers
http.authenticationProvider( customAuthenticationProvider() );
http.authenticationProvider( authenticationProvider() );
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/?lang=**").permitAll()
.antMatchers("/login**").permitAll()
.antMatchers("/about**").permitAll()
.antMatchers("/index.html").permitAll()
.antMatchers("/css/**").permitAll()
.antMatchers("/js/**").permitAll()
.antMatchers("/img/**").permitAll()
.antMatchers("/fonts/**").permitAll()
.antMatchers("/errors/**").permitAll()
.antMatchers("/error/**").permitAll()
.antMatchers("/webjars/**").permitAll()
.antMatchers("/static/**").permitAll()
.antMatchers("/information/**").permitAll()
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/home")
.failureUrl("/login?failedlogin=true")
.usernameParameter("username").passwordParameter("password")
.successHandler(authenticationSuccessHandler)
.failureHandler(authenticationFailureHandler)
.permitAll()
.and()
.logout()
.deleteCookies("JSESSIONID")
.addLogoutHandler(getCustomLogoutSuccessHandler())
.invalidateHttpSession(true)
.permitAll()
.and()
.csrf().disable()
.exceptionHandling()
.and()
.rememberMe().rememberMeParameter("remember-me").tokenRepository(tokenRepository)
.tokenValiditySeconds(86400)
;
}
....
}
AuthenticationSuccessHandler class
@Component("CustomAuthenticationSuccessHandler")
public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
/** Logger */
private static final Logger logger = LogManager.getLogger(CustomAuthenticationSuccessHandler.class);
@Autowired
private LocaleResolver localeResolver;
@Autowired
@Qualifier("UserService")
private UserService userService;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
logger.debug("CustomAuthenticationSuccessHandler.onAuthenticationSuccess called");
setLocale(authentication, request, response);
super.onAuthenticationSuccess(request, response, authentication);
}
protected void setLocale(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
logger.debug("CustomAuthenticationSuccessHandler.setLocale called");
if (authentication != null &&authentication.getPrincipal() != null) {
String username = (String) authentication.getPrincipal();
if (username != null) {
String localeOption = userService.getUsersPreferedLocaleOption(username);
logger.debug("localeOption " + localeOption);
if (localeOption != null && !localeOption.isEmpty()) {
Locale userLocale = Locale.forLanguageTag(localeOption);
localeResolver.setLocale(request, response, userLocale);
}
}
}
}
}
Part of html page that shows the language change options:
<form id="change-language-form" name="change-language-form" class="change-language-form ng-pristine ng-valid" method="POST">
<div class="row">
<div class="text-right col-sm-offset-8 col-sm-4">
<select id="language-selector" name="language-selector" class="language-selector">
<option value="">Language </option>
<option value="?lang=en_GB">English </option>
<option value="?lang=zh">中文 </option>
<option value="?lang=de_DE">Deutsch </option>
<option value="?lang=es_ES">Español </option>
<option value="?lang=fr_FR">Français </option>
</select>
</div>
</div>
<div class="row">
<div class="spacer-sml"></div>
</div>
</form>
Javascript that changes the language option:
$('#language-selector').change(function() {
var languageSelectedUrl = $(this).find("option:selected").val();
if (languageSelectedUrl) {
window.location = languageSelectedUrl;
}
});
You can implement LocaleResolver interface to bind users Locale to database. Sample implementation "ApplicationLocaleResolver.java" is below
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import net.app.locale.service.UserService;
@Configuration
public class ApplicationLocaleResolver extends SessionLocaleResolver {
@Autowired
UserService userService;
@Override
public Locale resolveLocale(HttpServletRequest request) {
SecurityContext securityContext = SecurityContextHolder.getContext();
String userName = securityContext.getAuthentication().getName();
String localeOption = userService.getUsersPreferedLocaleOption(userName);
Locale userLocale = Locale.forLanguageTag(localeOption);
return userLocale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
super.setLocale(request, response, locale);
SecurityContext securityContext = SecurityContextHolder.getContext();
String userName = securityContext.getAuthentication().getName();
userService.saveUsersPreferedLocaleOption(userName, locale);
}
}
I assume your userService has a method that save users local to db. I prefer userService.saveUsersPreferedLocaleOption(userName, locale);
You can change it.
Then you can replace localeResolver bean definition as below.
@Bean(name = "localeResolver")
public LocaleResolver localeResolver() {
return new ApplicationLocaleResolver();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With