Java 登录时更改区域设置
我想在登录到用户帐户SpringMVC应用程序(3.0)和SpringSecurity(3.0)中存储的默认区域设置后更改区域设置 我已经使用了Java 登录时更改区域设置,java,spring,internationalization,spring-security,Java,Spring,Internationalization,Spring Security,我想在登录到用户帐户SpringMVC应用程序(3.0)和SpringSecurity(3.0)中存储的默认区域设置后更改区域设置 我已经使用了LocaleChangeInterceptor,因此(未登录以及已登录)用户可以更改其区域设置(默认情况下从accept标头开始)。但是客户真的想要特定于帐户的默认值 所以我的问题是,登录后更改区域设置的最佳方法是什么,或者Spring/Security中是否已经有一些内置功能 使用,并将其构造为名为“localeResolver”的bean。此Loca
LocaleChangeInterceptor
,因此(未登录以及已登录)用户可以更改其区域设置(默认情况下从accept标头开始)。但是客户真的想要特定于帐户的默认值
所以我的问题是,登录后更改区域设置的最佳方法是什么,或者Spring/Security中是否已经有一些内置功能 使用,并将其构造为名为“localeResolver”的bean。此LocaleResolver将通过首先检查构造解析程序时使用的默认区域设置来解析区域设置。如果为空,它将检查会话中是否存储了区域设置,如果为空,它将根据请求中的Accept Language标头设置会话区域设置
用户登录后,您可以调用localeResolver.setLocale来为会话存储区域设置,您可以在servlet筛选器中执行此操作(请确保在spring安全筛选器之后在web.xml中定义它)
要从过滤器中访问localeResolver(或其他bean),请在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,执行任何业务逻辑来定义该用户的区域设置,并在LocaleResolver上调用setLocale
就我个人而言,我不希望SessionLocaleResolver首先使用默认的local(我更喜欢last),但是它确实很容易扩展和重写。如果您对检查会话、请求和默认设置感兴趣,请使用以下选项:
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;
}
}
我当前的Workaround就是这样工作的(但仍然是一个黑客,因为它不是由登录过程触发的): 我有一个Spring HandlerInterceptor,可以拦截每个请求。 它总是检查用户会话中是否已经有标志(
LOCALE\u ready\u SET\u SESSION\u属性
)指示本地已更新。
如果没有这样的标志,那么拦截器将检查请求是否属于经过身份验证的用户。
如果它是经过身份验证的用户,则会通过localResolver
更新本地解析程序,并在会话中设置标志(LOCALE\u ready\u set\u SESSION\u属性
)
这个会话标志是必需的,因为只有在登录后才能直接更改本地标志。因此,稍后用户可以通过普通的本地更改拦截器再次更改本地更改
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);
}
}
}
我能找到的最佳解决方案是在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>
由于您已经有了更改区域设置的机制,因此可以创建一个自定义设置来拦截登录并根据用户偏好更改区域设置。查看和以了解更多信息。关键问题不是应该调用哪个方法来更改本地。关键问题是在登录后触发一些方法(可以访问登录详细信息(用户名)和本地解析程序来更改本地名称),抱歉,但这并不能解决问题。登录后不会触发进程。在用户登录后,过滤器将根据每个请求更改用户的本地地址。这不是我被要求的:我使用LocaleChangeInterceptor这意味着用户可以独立于任何默认设置更改其登录名,特定于用户的本地仅为默认设置-用户必须能够稍后通过该interceptor更改它。--无论如何,我已经实现了这样一个解决方案(使用Spring拦截器而不是ServletFilter),但这是一个黑客攻击。谢谢:这个想法会奏效,我对它进行了一些修改,以使用事件(与InteractiveAuthenticationSuccessEvent和AuthenticationSuccessEvent相反,它包含请求和响应对象)我添加了完整性配置片段,以指示可以在何处以及如何设置authenticationSuccessHandler
如果您不喜欢,请从答案中删除该部分——很抱歉,我编辑了您的答案,但它有太多的文字用于注释。似乎不再有效,LocalResolver始终为空