我希望在Spring MVC应用程序(3.0)与Spring Security(3.0)中,登录后更改区域设置为存储在用户账户中的默认区域设置。
我已经使用了LocaleChangeInterceptor
,因此可以让用户(无论是登录用户还是未登录用户)更改其区域设置(默认情况下从接受标头获取)。但是客户确实希望有特定于账户的默认值。
所以我的问题是,在登录后更改区域设置的最佳方法是什么,或者Spring / Security中是否已经内置了某些功能?
我希望在Spring MVC应用程序(3.0)与Spring Security(3.0)中,登录后更改区域设置为存储在用户账户中的默认区域设置。
我已经使用了LocaleChangeInterceptor
,因此可以让用户(无论是登录用户还是未登录用户)更改其区域设置(默认情况下从接受标头获取)。但是客户确实希望有特定于账户的默认值。
所以我的问题是,在登录后更改区域设置的最佳方法是什么,或者Spring / Security中是否已经内置了某些功能?
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>
authenticationSuccessHandler
的设置位置和方式。如果您不喜欢它,请从答案中删除该部分。很抱歉我编辑了您的答案,但是对于评论来说,这是太多的文本了。 - Ralph@Override
public void init(FilterConfig fc) throws ServletException {
ServletContext servletContext = fc.getServletContext();
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
this.localeResolver = context.getBean(SessionLocaleResolver.class);
}
然后在doFilter方法中,您应该能够将ServletRequest强制转换为HttpServletRequest,调用getRemoteUser,执行任何业务逻辑来定义用户的区域设置,并调用LocaleResolver上的setLocale。
个人而言,我不喜欢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;
}
}
我的当前解决方法是这样的(但仍然是一个hack,因为它不是由登录过程触发的):
我有一个Spring HandlerInterceptor拦截每个请求。
它总是检查用户会话中是否已经有一个标志(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);
}
}
}
AuthenticationSuccessHandler
来拦截登录并根据用户偏好更改区域设置。请查看这里和这里获取更多信息。 - bluefoot