Spring Security + i18n =如何使其协同工作?


问题内容

我在这里的第一个问题,我会尽量具体。我对Spring还是很陌生,我正在尝试创建一个非常简单的预订系统(但这实际上没有关系)。重要的是,我正在创建一些基本模板,然后将它们填充到真实的网页中。应用程序可以在hibernate,MySQL上运行,我还可以设置i18n和spring安全性。问题是我无法更改语言环境。唯一有效的方法是更改​​默认值。首先,我浏览了Web
A LOT,然后发现与弹簧安全性一起使用i18n通常比通常更复杂。我发现我需要附加过滤器:

<filter>
    <filter-name>localizationFilter</filter-name>
    <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>localizationFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

我发现该过滤器确实在安全过滤器之前处理过,但是它没有以以下形式解析请求:http://someserver.com/bla/home?locale=en。我调试了它,似乎它不是出于这种目的而创建的(这就是我所需要的)。这取自spring示例“联系人”,但是在此示例中,我找不到更改语言实际上针对的任何代码。结果是它根本不起作用。它总是尝试将语言环境更改为我的默认语言环境。好消息是,如果我在调试模式下手动将语言环境设置更改为其他语言环境,则它可以正常工作,因此我心中充满希望…
;-)

然后我找到了其他方法-
通过创建我们自己的过滤器。我所做的是将找到的示例(不要记住作者)与RequestContextFilter创建方式合并在一起。一切RequestContextFilter工作正常之后-
请不要解析我的请求。那是新过滤器的代码:

public class InternationalizationFilter extends OncePerRequestFilter {

@Override
public void destroy() {
    // TODO Auto-generated method stub

}


@Override
protected void doFilterInternal(final HttpServletRequest request,
        final HttpServletResponse response, final FilterChain filterChain)
        throws ServletException, IOException {
    final String newLocale = request.getParameter("locale");
    if (newLocale != null) {
        final Locale locale = StringUtils.parseLocaleString(newLocale
                .toLowerCase());
        LocaleContextHolder.setLocale(locale);
    }
    try {
        filterChain.doFilter(request, response);
    } finally {

        LocaleContextHolder.resetLocaleContext();
    }
}

}

如您所见,解析了请求参数语言环境并设置了语言环境。有2个问题:1.发送请求后,xxxxx?locale=en它创建的语言环境没有“
country”属性(仅设置了语言)。老实说,我不知道这是否有问题-
也许不是。2.更严重的问题是它不起作用…我的意思是它在筛选器链中的正确位置(在安全性之前),它会产生正确的语言环境并以与RequestContextFilter
完全相同的方式进行设置。但它根本行不通。

如果有人可以根据我给出的示例或任何其他信息,让我知道如何使i18n具备弹簧安全性,我将非常高兴。

谢谢!

其他信息:我做了一些实验,似乎来自请求的Locale实例在某种程度上是特定的。

看下面的代码(修改RequestContextFilter类):

    @Override
protected void doFilterInternal(final HttpServletRequest request,
        final HttpServletResponse response, final FilterChain filterChain)
        throws ServletException, IOException {

    final ServletRequestAttributes attributes = new ServletRequestAttributes(
            request);
    final Locale l = Locale.GERMAN;
    final Locale l2 = request.getLocale();
    LocaleContextHolder.setLocale(l,
            this.threadContextInheritable);
    RequestContextHolder.setRequestAttributes(attributes,
            this.threadContextInheritable);
    if (logger.isDebugEnabled()) {
        logger.debug("Bound request context to thread: " + request);
    }
(...)

如果使用此方法:LocaleContextHolder.setLocale(l, this.threadContextInheritable);
我通过语言环境’l’则根本不起作用。我的意思是即使您明确更改了语言环境也不会更改。另一方面,如果我在此处传递的语言环境“
l2”被修改为德语(在调试模式下),则可以正常工作!

这意味着出于某种原因,本地语言实例从request.getLocale()某种程度上受到青睐,也许稍后代码中发生了我不知道/不了解的事情……

请让我知道如何将i18n与安全原因一起使用,以至于我必须承认我不知道发生了什么…

-====-======-======-=======-====

最终解决方案/答案
(但仍然有一点疑问)感谢Ralph,我设法解决了这个问题。以前,我走错了方向,但是项目清单生成的项目推动了我前进。似乎我一直以错误/不准确的方式添加拦截器(先前的代码):

<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
</bean>
<bean id="localeResolver"
    class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
    <property name="defaultLocale" value="pl"/>
</bean>
<bean id="handlerMapping"
    class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="interceptors">
        <ref bean="localeChangeInterceptor" />
    </property>
</bean>

这样,拦截器由于某种原因就不会被调用。

将拦截器def更改为:

<mvc:interceptors>
<bean id="localeChangeInterceptor"
class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
</bean>
</mvc:interceptors>

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

<bean id="handlerMapping"
    class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
</bean>

…在没有对security / web.xml进行任何其他更改的情况下,它开始可以正常工作。

现在问题已经解决了,但是我不确定发生了什么。根据我在第二个示例(有效的示例)中了解到的,我将拦截器设为“全局”。但是,为什么在第一个示例中定义的拦截器不起作用?有什么提示吗?

再次感谢您的帮助!N.


问题答案:
  1. 发送请求xxxxx?locale = en后,它将创建不带“国家/地区”属性的语言环境(仅设置语言)。

这是预期的行为。在Java中,存在某种层次结构。语言比国家更通用。

背后的想法是,例如,您可以使用更通用的语言显示文本,但可以在特定于国家/地区的文件中包含某些单位(例如货币)。

@see:http :
//java.sun.com/developer/technicalArticles/Intl/IntlIntro/


  1. 更严重的问题是它不起作用…

它应该无需任何手工实现即可工作!

您需要注册本地更改拦截器,并需要为登录页面设置permitAll。

<mvc:interceptors>         
     <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="lang"/>
</mvc:interceptors>

<http auto-config="true" use-expressions="true">
    <form-login login-processing-url="/resources/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t"/>
    <logout logout-url="/resources/j_spring_security_logout"/>

    <!-- Configure these elements to secure URIs in your application -->
    <intercept-url pattern="/login" access="permitAll" />
    <intercept-url pattern="/resources/**" access="permitAll" />
    <intercept-url pattern="/**" access="isAuthenticated()" />
</http>

要查看此示例的运行情况,请使用该roo脚本创建一个roo项目:

// Spring Roo 1.1.5.RELEASE [rev d3a68c3] log opened at 2011-12-13 09:32:23
project --topLevelPackage de.humanfork.test --projectName localtest --java 6
persistence setup --database H2_IN_MEMORY --provider HIBERNATE 
ent --class ~.domain.Stuff
field string --fieldName title
controller all --package ~.web
security setup
web mvc language --code de
web mvc language --code es

然后,您只能更改安全过滤器的跨URL模式,如我在上面(applicationContext-security.xml)所示!

现在,您有了一个应用程序,用户可以通过应用程序中的本地更改拦截器(当用户登录时)以及未登录时(在登录页面中)更改其本地用户