Сохраняйте локализацию при тайм-ауте весеннего сеанса безопасности

#java #spring #jakarta-ee #spring-mvc #spring-security

#весна #spring-mvc #spring-безопасность

Вопрос:

Я использую Spring Security 3.0.2, и у меня есть эта конфигурация

     <security:form-login
        login-processing-url="/resources/j_spring_security_check"
        login-page="/login"
         authentication-failure-handler-ref="authErrorHandler" authentication-success-handler-ref="authCorrectHandler" />
    <security:logout logout-success-url="/index.jsp" invalidate-session="false" />
    <security:remember-me />
    <security:session-management invalid-session-url="/login/sessionExpired" >
        <security:concurrency-control max-sessions="1"
            error-if-maximum-exceeded="true" />
    </security:session-management>
 

Когда я вхожу в систему с определенной локализацией, все прошло хорошо, но по истечении срока действия сеанса Spring Security очищает сеанс и создает новый анонимный сеанс с локализацией по умолчанию (и переходит на страницу входа, как и ожидалось). В результате пользовательская локаль была ПОТЕРЯНА.

Как я могу сохранить локаль пользователя при истечении сеанса в Spring Security 3.0.2?

Я использую LocaleChangeInterceptor для установки локали, например:

     <bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
        <property name="paramName" value="language" />
    </bean>
 

и SessionLocaleResolver в качестве средства определения локали:

 <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
    <property name="defaultLocale" value="es" />
</bean>
 

РЕДАКТИРОВАТЬ — РЕШАЕМАЯ ДЛЯ МОИХ НУЖД

Я решил эту проблему, окончательно установив файл cookie в моем собственном LocaleChangeInterceptor, который расширяется от HandlerInterceptorAdapter, записав это в метод предварительной обработки:

      LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
     if (localeResolver == null) {
       throw new IllegalStateException("No LocaleResolver found.");
     }
     Cookie mylang = new Cookie("mylang", locale.getLanguage()   "_"   locale.getCountry());
     mylang.setMaxAge(86400); 
     response.addCookie(mylang);

     localeResolver.setLocale(request, response, locale);
 

а затем в точке контроллера / sessionExpired я получаю значение cookie:

 public String sessionExpired(HttpServletRequest request, HttpServletResponse response, Model model, 
        @CookieValue(value = "mylang", defaultValue = "es_ES") String myLang) throws Exception {
    model.addAttribute("mylang", myLang);
    LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);

    StringTokenizer st = new StringTokenizer(myLang, "_");

    String language = "";
    String country = "";

    try {
        language = (String) st.nextElement();
        country = (String) st.nextElement();
    } catch (Exception e) {
        throw new Exception("Error locale");
    }

    Locale locale = new Locale(language, country); 
    localeResolver.setLocale(request, response, locale);
    return "sessionExpired";
 

В этом случае нет необходимости использовать базу данных в качестве временного хранилища.

Комментарии:

1. Можете ли вы опубликовать, как вы устанавливаете локаль?

2. обновленный пост, показывающий, как я устанавливаю локаль

3. Логически не похоже, что вы можете использовать сеанс для отслеживания локали. Локаль устанавливается с использованием параметра запроса language , который будет очищен с сеансом. При создании нового сеанса по умолчанию используется локаль es , заданная вашим localeResolver . Один из способов, о котором я мог подумать, — сохранить пользовательские настройки в базе данных и извлекать их оттуда при последующих входах в систему.

4. Правильно, это то, о чем я думаю. Но я надеялся, что у Spring Security может быть какой-либо механизм для сохранения, а не уничтожения локали.

5. Вы используете SessionLocaleResolver which, очевидно, сохраняет его в сеансе. Нет сеанса, нет сохраненной локали. Если у вас все в порядке с файлами cookie, используйте параметр CookieLocaleResolver , который сохраняет выбор в файле cookie. Еще реализуйте свой собственный LocaleResolver , который сохраняет / извлекает из базы данных.

Ответ №1:

Логически не похоже, что вы можете использовать сеанс для отслеживания локали. Локаль устанавливается с использованием параметра запроса language , который будет очищен с сеансом. При создании нового сеанса по умолчанию используется локаль es , заданная вашим localeResolver . Один из способов, о котором я мог подумать, — сохранить пользовательские настройки в БД и извлекать их оттуда при последующих входах в систему

Также, как было предложено @M. Deinum:

Если у вас все в порядке с файлами cookie, используйте параметр CookieLocaleResolver , который сохраняет выбор в файле cookie. Еще реализуйте свой собственный LocaleResolver , который сохраняет / извлекает из базы данных.

Комментарии:

1. Я не знаю, почему, но CookieLocaleResolver сам по себе не работает для меня во время входа в систему, сохраните предыдущую сохраненную локаль вместо текущей. В любом случае, тайм-аут сеанса работает должным образом и сохраняет локаль. Позвольте мне проверить проблему во время входа в систему.

2. Я обновил вопрос с помощью решения, работающего для меня. Спасибо всем за вашу помощь 🙂

Ответ №2:

Наткнулся на этот старый вопрос и просто подумал поделиться тем, как я справился со своим затруднительным положением, используя a Filter (предпочел бы an Interceptor , но по какой-то причине это раздражающе не работает для меня на момент написания этой статьи).

 public class SessionTimeoutFilter implements Filter {

    private String contextPath;

    private String language;

    @Override
    public void init(FilterConfig config) {
        contextPath = config.getInitParameter("context.path");
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        String path = request.getRequestURI();

        if (path.equals(contextPath)) { //the index page
            request.getSession(true);
            chain.doFilter(request, response);
        } else {
            HttpSession session = request.getSession(false);

            if (session != null) {
                language = (String) session.getAttribute("lang");
                chain.doFilter(request, response);
            } else {
                response.sendRedirect(contextPath   "?timeout=trueamp;lang="   language);
            }
        }
    }

}
 

И, кроме LocaleChangeInterceptor того, у меня были параметры в моем Controller .

 @GetMapping("/")
public String home(@RequestParam(value = "timeout", required = false) Boolean timeout,
                   @RequestParam(value = "lang", required = false) String language,
                   HttpServletRequest request, Model model) {
    if (Strings.isNotBlank(language)) {
        LocaleContextHolder.setLocale(new Locale(language));
    } else {
        language = LocaleContextHolder.getLocale().getLanguage();
    }

    if (BooleanUtils.isTrue(timeout)) {
        String message = messageService.getMessage("error.timeout", null);
        model.addAttribute("message", message);
    }

    request.getSession().setAttribute("lang", language);
    return "index";
}