Change locale on login Change locale on login spring spring

Change locale on login


The best solution I was able to find was to handle this in the AuthenticationSuccessHandler.

The following is some code I wrote for my startup:

public class LocaleSettingAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {    @Resource    private LocaleResolver localeResolver;    @Override    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {        setLocale(authentication, request, response);        super.onAuthenticationSuccess(request, response, authentication);    }    protected void setLocale(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {        if (authentication != null) {            Object principal = authentication.getPrincipal();            if (principal instanceof LocaleProvider) {                LocaleProvider localeProvider = (LocaleProvider) principal;                Locale providedLocale = localeProvider.getLocale();                localeResolver.setLocale(request, response, providedLocale);            }        }    }}

And the following interface should be offered by your principal class. This is not needed but I'm using it since I have multiple objects capable of providing a locale for the session.

public interface LocaleProvider {        Locale getLocale();    }

Configuration snippets:

<security:http ...>    <security:custom-filter ref="usernamePasswordAuthenticationFilter" position="FORM_LOGIN_FILTER"/></security:http><bean id="usernamePasswordAuthenticationFilter"    class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">    <property name="filterProcessesUrl" value="/login/j_spring_security_check"/>    <property name="authenticationManager" ref="authenticationManager"/>    <property name="authenticationFailureHandler">        <bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">            <property name="defaultFailureUrl" value="/login?login_error=t"/>        </bean>    </property>    <property name="authenticationSuccessHandler">        <bean class="LocaleSettingAuthenticationSuccessHandler">    </property></bean>


Use the SessionLocaleResolver, and construct it as a bean called "localeResolver". This LocaleResolver will resolve locales by first checking the default locale the resolver was constructed with. If that's null, it will check if a locale has been stored in the session, and if that's null, it will set the session locale based on the Accept-Language header in the request.

After a user is logged in, you can call localeResolver.setLocale to store a locale to the session for you, you can do this in a servlet filter (be sure to define it in your web.xml AFTER your spring security filter).

To gain access to your localeResolver (or other beans) from your filter, do something like this in the init method:

@Overridepublic void init(FilterConfig fc) throws ServletException {    ServletContext servletContext = fc.getServletContext();    ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);    this.localeResolver = context.getBean(SessionLocaleResolver.class);}

Then in the doFilterMethod, you should be able to cast the ServletRequest to an HttpServletRequest, call getRemoteUser, perform any business logic to define that user's locale, and call setLocale on the LocaleResolver.

Personally, I do not care for the SessionLocaleResolver to use the default local first (I prefer last), however it is really easy to extend and override. If you are interested in checking the session, then the request, then the default, use the following:

import org.springframework.stereotype.Component;import org.springframework.web.util.WebUtils;import javax.servlet.http.HttpServletRequest;import java.util.Locale;// The Spring SessionLocaleResolver loads the default locale prior// to the requests locale, we want the reverse.@Component("localeResolver")public class SessionLocaleResolver extends org.springframework.web.servlet.i18n.SessionLocaleResolver{    public SessionLocaleResolver(){        //TODO: make this configurable        this.setDefaultLocale(new Locale("en", "US"));    }    @Override    public Locale resolveLocale(HttpServletRequest request) {        Locale locale = (Locale) WebUtils.getSessionAttribute(request, LOCALE_SESSION_ATTRIBUTE_NAME);        if (locale == null) {            locale = determineDefaultLocale(request);        }        return locale;    }    @Override    protected Locale determineDefaultLocale(HttpServletRequest request) {        Locale defaultLocale = request.getLocale();        if (defaultLocale == null) {            defaultLocale = getDefaultLocale();        }        return defaultLocale;    }}


My current workarround works this way (but is is still a hack, because it is not triggered by the login process):

I have a Spring HandlerInterceptor that intercepts every request.It checks always if there is already a flag (LOCALE_ALREADY_SET_SESSION_ATTRIBUTE) in the users session that indicate that the local is already updated.If there is no such flag, then the interceptor checks if the request belongs to an authenticated user.If it is an authenticated user then it updates the local though the localResolver and set the flag (LOCALE_ALREADY_SET_SESSION_ATTRIBUTE) in the session

This session flag stuff is needed, because the local must be changed only direclty after login. So later on the user can change the local again through the normal local change interceptor.

public class LocalChangeUserInterceptor extends HandlerInterceptorAdapter {    /** Session key, used to mark if the local is set. */    private static final String LOCALE_ALREADY_SET_SESSION_ATTRIBUTE = "LocalChangeUserInterceptor.localeAlreadySet";    /** The locale resolver. */    @Resource    private LocaleResolver localeResolver;    @Resource    private UserService userService;    @Override    public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler)            throws Exception {        if (!isLocaleAlreadySet(request)) {            User currentAuthenticatedUser = getCurrentUserOrNull();            if (currentAuthenticatedUser != null) {                this.localeResolver.setLocale(request, response, currentAuthenticatedUser.getLocale());                request.getSession().setAttribute(LOCALE_ALREADY_SET_SESSION_ATTRIBUTE, "true");            }        }        return true;    }    /**     * Check if there is an session attribute that states that the local is already set once.     * @param request the request     * @return true, if is locale already set     */    private boolean isLocaleAlreadySet(final HttpServletRequest request) {        HttpSession sessionOrNull = request.getSession(false);        return ((sessionOrNull != null) && (sessionOrNull.getAttribute(LOCALE_ALREADY_SET_SESSION_ATTRIBUTE) != null));    }     /**     * Get the current user or null if there is no current user.     * @return the current user     */    public User getCurrentUserOrNull() {        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();        if ((authentication == null) || (authentication instanceof AnonymousAuthenticationToken)) {            return null;        } else {            return this.userService.getUser(authentication);        }    }}