2011-12-16 130 views
10

我想在使用Spring Security(3.0)登录到存储在用户帐户Spring MVC Application(3.0)中的默认语言环境后更改语言环境。登录时更改语言环境

我已经使用LocaleChangeInterceptor,所以a(未登录,以及登录)用户可以更改其语言环境(默认情况下来自accept标头)。但客户真的想要这个帐户特定的默认值。

所以我的问题是,登录后更改区域设置的最佳方式是什么,或者Spring/Security中是否已经有一些内置功能?

+1

既然你已经有机制来改变语言环境,您可以创建自定义[`AuthenticationSuccessHandler`](http://static.springsource.org/spring- security/site/docs/3.0.x/apidocs/org/springframework/security/web/authentication/AuthenticationSuccessHandler.html)拦截登录并根据用户首选项更改区域设置。检查[这里](http://static.springsource.org/spring-security/site/docs/3.0.x/reference/springsecurity-single.html#nsa-form-login)和[here](http:// stackoverflow.com/a/6612634/468508)了解更多信息。 – bluefoot 2011-12-16 19:11:31

回答

8

我能找到的最佳解决方案是在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,并将其构造为一个名为“localeResolver”的bean。此LocaleResolver将通过首先检查解析器构造的默认语言环境来解析区域设置。如果它为空,它将检查区域设置是否已存储在会话中,如果为空,它将根据请求中的Accept-Language标头设置会话区域设置。

用户登录后,您可以调用localeResolver.setLocale为您的会话存储语言环境,您可以在servlet过滤器中执行此操作(请确保在您的web.xml中定义它,过滤)。

为了从你的过滤器访问您的LocaleResolver(或其他豆类),这样做在init方法是这样的:在doFilterMethod

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

然后,你应该能够到的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; 
    } 

} 
+0

关键的问题是不应该调用改变本地的方法。关键问题是在登录后触发某种方法(可以访问登录详细信息(用于用户名)和本地解析器更改本地) – Ralph 2012-01-04 14:25:57

+0

我更新了上面的答案。 – aweigold 2012-01-04 15:22:44

0

我目前workarround以这种方式工作(但仍是一个黑客,因为它不是由登录触发过程):

我有一个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); 
     } 
    } 
}