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