4

Spring (MVC)、Hibernate、Spring Security、ZK をフロントエンドとして使用する Web アプリケーションに取り組んでいます。すべてのライブラリの最新リリース (3.1.2 Spring、3.1.3 Spring Security、4.1.7 Hibernate) を使用していますが、国際化 (i18n) に問題があります。構成後に詳細を説明します (関連する部分のみ)。

web.xml:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/root-context.xml /WEB-INF/spring/spring-security.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/appServlet/servlet-context.xml 
        </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>zkLoader</servlet-name>
    <servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class>
    <init-param>
        <param-name>update-uri</param-name>
        <param-value>/zkau</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet>
    <servlet-name>auEngine</servlet-name>
    <servlet-class>org.zkoss.zk.au.http.DHtmlUpdateServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>zkLoader</servlet-name>
    <url-pattern>*.zul</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>auEngine</servlet-name>
    <url-pattern>/zkau/*</url-pattern>
</servlet-mapping>

<listener>
    <description>ZK JSP Tags environment initiation </description>
    <display-name>ZK JSP Initiator</display-name>
    <listener-class>org.zkoss.jsp.spec.JspFactoryContextListener</listener-class>
</listener>

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class> org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

サーブレット コンテキスト:

<beans:bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
    <beans:property name="defaultLocale" value="hr" />
</beans:bean>

<interceptors>
    <beans:bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
        <beans:property name="paramName" value="lang" />
    </beans:bean>
</interceptors>

<beans:bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <beans:property name="basename" value="classpath:message" />
</beans:bean>

春のセキュリティ.xml:

<http pattern="/resources/**" security="none" />

<http auto-config="true">
    <intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
    <intercept-url pattern="/**" access="ROLE_USER" />
    <form-login login-page="/login" default-target-url="/" authentication-failure-url="/loginfailed" />
</http>

root-context.xml:

ここでは問題に関連するものは何もありません。データソースの定義、sessionfactory、および Bean の宣言だけです。

次に問題に進みます。

src/main/resources ディレクトリにある message_en.properties と message_hr.properties の 2 つのファイルがあります。「Spring テンプレート プロジェクト」を使用してこのプロジェクトを作成し、「Spring MVC プロジェクト」を選択しました (STS 2.9.2 を使用)。Spring Security メッセージをカスタマイズする方法について読みましたが、オーバーライドする必要があるメッセージは、カスタム メッセージが添付された message.properties ファイルに入れられます。私がテストに使用しているものは次のとおりです。

spring-security-core.jar の元のメッセージ AbstractUserDetailsAuthenticationProvider.badCredentials = 不正な認証情報

message_en.properties でオーバーライド:

AbstractUserDetailsAuthenticationProvider.badCredentials = 無効なユーザー名またはパスワードです

message_hr.properties でオーバーライド:

AbstractUserDetailsAuthenticationProvider.badCredentials = Bla Bla Bla

シナリオ 1:

上記のように構成ファイルにすべてを残し、URL バーの lang パラメータを変更するか、ログイン ページにあるリンクをクリックするだけで、SS のものを除くカスタム message_xx.properties からすべてのメッセージを適切に読み取ります。そのため、「無効なユーザー名またはパスワード」または「Bla Bla Bla」を提供する代わりに、「Bad credentials」が表示されます。

シナリオ 2:

messageSource Bean を servlet-context.xml から spring-security.xml に移動すると、適切なエラー メッセージが読み込まれますが、設定されているロケールに関係なく、常に「Bla Bla Bla」と表示されます。これは、localeChangeInterceptor Bean の lang パラメータを変更しても発生します。

これを適切に機能させるには、ここで何をすればよいでしょうか?

言及するのを忘れました:Spring Securityメッセージを取得するために、これをjspページで使用しています

${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}

4

2 に答える 2

4

同じ問題が発生しました。Spring Security i18n からこれを読んだ後、次のフィルターを作成しました。

  1. Cookie からロケールを取得します (私は CookieLocaleResolver を使用していますが、これは SessionLocaleResolver のような他のタイプの localeResolver で実行できます)
  2. LocaleContextHolder.setLocale(locale, true) を呼び出します
  3. chain.doFilter の前。

このフィルターは、web.xml 内の springSecurityFilterChain のフィルター マッピングの前に表示する必要があります。そのようにして、spring-security が LocaleContextHolder.getLocale() を呼び出すと、正しいものが取得されます。あなたの場合、org.springframework.web.servlet.i18n.SessionLocaleResolver を使用しているため、セッションからロケールを取得できます。

Spring コンテキスト xml ファイル (代わりに SessionLocaleResolver を引き続き使用できます):

    <!-- Saves a locale change using a cookie -->
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver" />

Java フィルター コード (spring-security は LocaleContextHolder.getLocale を使用するため、重要な行は LocaleContextHolder.setLocale です):

package com.xyz;

import java.io.IOException;
import java.util.Locale;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.util.WebUtils;

/**
 * Transfert the value of user Locale into LocaleContextHolder which is used by spring-security
 */
public class FilterI18nCookie implements Filter {
    private static final Logger LOG = LoggerFactory.getLogger(FiltreI18nCookie.class);

    /**
     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
     *      javax.servlet.ServletResponse, javax.servlet.FilterChain)
     */
    public void doFilter(ServletRequest pRequest, ServletResponse pResponse, FilterChain pFilterChain) throws IOException, ServletException {
        if (!(pRequest instanceof HttpServletRequest)) {
            pFilterChain.doFilter(pRequest, pResponse);
            return;
        }

        HttpServletRequest request = (HttpServletRequest) pRequest;

        Cookie cookie = WebUtils.getCookie(request, CookieLocaleResolver.LOCALE_REQUEST_ATTRIBUTE_NAME);
        if (cookie != null) {
            Locale locale = org.springframework.util.StringUtils.parseLocaleString(cookie.getValue());
            if (locale != null) {
                LOG.info("Locale cookie: [" + cookie.getValue() + "] == '" + locale + "'");
                request.setAttribute(CookieLocaleResolver.LOCALE_REQUEST_ATTRIBUTE_NAME, locale);
                LocaleContextHolder.setLocale(locale, true);
            }
        }

        pFilterChain.doFilter(pRequest, pResponse);
    }

    /**
     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
     */
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    /**
     * @see javax.servlet.Filter#destroy()
     */
    public void destroy() {
    }

}

web.xml ファイル内 (カスタム フィルター FilterI18nCookie のフィルター マッピング定義は、springSecurityFilterChain の前に適用されます):

<filter>
    <filter-name>FilterI18nCookie</filter-name>
    <filter-class>com.xyz.FilterI18nCookie</filter-class>
</filter>
    <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>
<filter-mapping>
    <filter-name>FilterI18nCookie</filter-name>
    <url-pattern>/ *</url-pattern>
</filter-mapping>
<filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/ *</url-pattern>
</filter-mapping>
<servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
于 2013-04-15T16:12:15.710 に答える
0

A.Masson の提案に基づいて、あらゆるタイプの LocalResolver 実装をサポートするように変更を加えました。

/**
 * Transfert the value of user Locale into LocaleContextHolder which is used by spring-security
 */
public class FilterI18nSpringSecurity implements Filter {
    private static final Logger LOG = LoggerFactory.getLogger(FilterI18nSpringSecurity.class);

    private WebApplicationContext springContext;

    /**
     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
     *      javax.servlet.ServletResponse, javax.servlet.FilterChain)
     */
    public void doFilter(ServletRequest pRequest, ServletResponse pResponse, FilterChain pFilterChain) throws IOException, ServletException {
        if (!(pRequest instanceof HttpServletRequest)) {
            pFilterChain.doFilter(pRequest, pResponse);
            return;
        }

        LocaleResolver bean = springContext.getBean(LocaleResolver.class);
        Locale locale = bean.resolveLocale((HttpServletRequest) pRequest);
        LOG.info("Locale -> " + locale);
        LocaleContextHolder.setLocale(locale, true);

        pFilterChain.doFilter(pRequest, pResponse);
    }

    /**
     * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
     */
    public void init(FilterConfig filterConfig) throws ServletException {
        springContext = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
    }

    /**
     * @see javax.servlet.Filter#destroy()
     */
    public void destroy() {
    }

}

編集:以前のアプローチは、状況によっては機能しませんでした。私はこれに変更し、より良い動作を得ました。Spring 例外クラス名をメッセージ キーとして使用したので、これらのメッセージを現在のプロパティ ファイルにマップできます。(私のテンプレートエンジンはタイムリーフです)

ページ内:

<th:block th:if="${session.SPRING_SECURITY_LAST_EXCEPTION != null &amp;&amp; param.error != null}">
    <div th:replace="fragments/alert :: alert (type='danger', message=#{${session.SPRING_SECURITY_LAST_EXCEPTION.class.name}})">Alert</div>
</th:block>

メッセージのプロパティ:

org.springframework.security.authentication.BadCredentialsException=Usuario o contraseña... org.springframework.security.authentication.LockedException=Usuario bloqueado org.springframework.security.authentication.DisabledException=El usuario está deshabilitado

于 2014-10-23T15:32:48.427 に答える