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; } });
Advertisement
Answer
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(); }