Изменить язык при входе в систему

Я хочу изменить локаль после входа в стандартную локаль, хранящуюся в учетной записи пользователя Spring MVC Application (3.0) с помощью Spring Security (3.0).

Я уже использую LocaleChangeInterceptor, так что (не вошедший в систему, а также вошедший в систему) пользователь может изменить свой язык (по умолчанию из заголовка accept). Но клиент действительно хочет, чтобы учетная запись была выполнена по умолчанию.

Итак, мой вопрос заключается в том, что было бы лучшим способом изменить языковой стандарт после входа в систему, или уже есть какая-то встроенная функциональность в Spring/Security?

Ответ 1

Лучшее решение, которое я смог найти, это обработать это в AuthenticationSuccessHandler.

Ниже приведен код, который я написал для моего запуска:

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);
            }
        }
    }
}

И ваш основной класс должен предложить следующий интерфейс. Это не нужно, но я использую его, так как у меня есть несколько объектов, способных предоставить локаль для сеанса.

public interface LocaleProvider {    
    Locale getLocale();    
}

Отделы конфигурации:

<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>

Ответ 2

Используйте SessionLocaleResolver и создайте его как bean, называемый "localeResolver". Этот LocaleResolver разрешит локали, предварительно проверив локаль по умолчанию, с которой был создан резольвер. Если это значение null, оно проверит, сохранен ли языковой стандарт в сеансе, и если этот нуль, он установит локаль сеанса на основе заголовка Accept-Language в запросе.

После входа пользователя в систему вы можете вызвать localeResolver.setLocale для хранения локали для сеанса для вас, вы можете сделать это в фильтре сервлета (обязательно определите его в своем web.xml ПОСЛЕ вашего spring фильтр безопасности).

Чтобы получить доступ к вашему localeResolver (или другому beans) из вашего фильтра, выполните следующие действия в методе init:

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

Затем в doFilterMethod вы сможете передать ServletRequest в HttpServletRequest, вызвать getRemoteUser, выполнить любую бизнес-логику для определения этого локали пользователя и вызвать setLocale в LocaleResolver.

Лично мне не нужно, чтобы SessionLocaleResolver сначала использовал локальную по умолчанию (я предпочитаю последний), однако ее очень легко расширить и переопределить. Если вы заинтересованы в проверке сеанса, тогда запрос, а затем по умолчанию, использует следующее:

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;
    }

}

Ответ 3

Мой текущий рабочий стол работает таким образом (но он все еще взломан, потому что он не запускается процессом входа):

У меня есть обработчик HandlerInterceptor Spring, который перехватывает каждый запрос. Он проверяет всегда, если в сеансе пользователей уже есть флаг (LOCALE_ALREADY_SET_SESSION_ATTRIBUTE), указывающий, что локаль уже обновлена. Если такого флага нет, тогда перехватчик проверяет, принадлежит ли запрос аутентифицированному пользователю. Если он является аутентифицированным пользователем, он обновляет локальный, но localResolver и устанавливает флаг (LOCALE_ALREADY_SET_SESSION_ATTRIBUTE) в сеансе

Этот флаговый файл сеанса необходим, поскольку локальный файл должен быть изменен только после входа в систему. Поэтому позже пользователь может снова изменить локаль через обычный локальный перехватчик изменений.

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);
        }
    }
}