0

私のシナリオは次のとおりです。Spring セキュリティを追加したばかりの Spring Web アプリケーション (webmvc を使用) があります。十分な権限を持つユーザーがユーザーを追加できる CRUD ユーザー マネージャー ページがあります。サーバーでは、これは com.myapp.UserController クラスによって処理されます。また、com.myapp.UserDetailsS​​erviceImpl と呼ばれる org.springframework.security.core.userdetails.UserDetailsS​​ervice の独自の実装も取得しました。UserController と私の UserDetailsS​​ervice impl はどちらも、java.util.List でユーザーを追跡する com.myapp.UserService を使用します。

私が見つけたのは、Web インターフェイスを介して新しいユーザーを作成し、ログアウトしてから、その新しいユーザーで再度ログインしようとしたときに、ユーザーが見つからないということでした。よく調べてみると、com.myapp.UserService の 2 つの異なるインスタンスがあるためです。UserController によって使用されるものには新しいユーザーがありますが、UserDetailsS​​erviceImpl によって使用されるものにはありません。

この webapp を起動すると、2 つの異なる Spring コンテナーが実行されるという事実まで原因を突き止めました。1 つは webmvc (myapp-servlet.xml) 用で、もう 1 つはセキュリティ (myapp-security.xml) 用です。それぞれに UserService の独自のインスタンスがあります。これは、Spring webmvc をセキュリティで構成するための非常に一般的なシナリオであることを理解しています。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">

    <servlet>
        <servlet-name>myapp</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>myapp</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

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

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/myapp-servlet.xml, /WEB-INF/myapp-security.xml</param-value>
    </context-param>

    <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>

</web-app>

私の myapp-servlet.xml では、@Service で注釈が付けられているため、UserService を取得するコンポーネント スキャンが進行中です。

    <context:component-scan base-package="com.myapp" />

ただし、UserService は myapp-security.xml によっても取得されます。これは、認証マネージャー構成の一部として参照されているためです。

    <authentication-manager>
        <authentication-provider user-service-ref="userDetailsService" />
    </authentication-manager>

完全を期すために、com.myapp.UserDetailsS​​erviceImpl の関連部分を次に示します。

@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    @Qualifier("testUserService")
    private UserService userService;

    ...
}

そして私のUserController:

@Controller
@RequestMapping("/admin/users")
public class UserController {

    @Autowired
    @Qualifier("testUserService")
    private UserService userService;

    ...
}

私の質問はこれです: webmvc とセキュリティの両方を 1 つのスプリング コンテナーに結合して、UserService のインスタンスが重複して作成されないようにする方法はありますか?

4

2 に答える 2

1

簡潔な答え

/WEB-INF/myapp-servlet.xmlを contextConfigLocation コンテキスト パラメータから削除します。

長い答え

ContextLoaderListener は、contextConfigLocationで定義された構成ファイルに基づいてルート アプリケーション コンテキストを作成し、サーブレットが初期化される前にそれを ServletContext にロードします。

DispatcherServlet は同時に、指定された構成で子アプリケーション コンテキストを作成します。Bean 定義ファイルを明示的に指定していないため、慣例により /WEB-INF/appName-servlet.xml (あなたの場合は /WEB-INF/myapp-servlet.xml が偶然存在します) になります。ルート アプリケーション コンテキストと子アプリケーション コンテキストの両方に、いくつかの共通の Bean がある場合があります (同じ構成ファイルをロードするため、重複します)。

次の 2 つのオプションがあります。

1) contextConfigLocation から /WEB-INF/myapp-servlet.xml を削除します。さらに、より明確にしたい場合は、構成を ServletDispatcher 宣言に追加します。

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

<servlet-mapping>
    <servlet-name>myapp</servlet-name>
    <url-pattern>*.html</url-pattern>
</servlet-mapping>

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

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

注: myapp-servlet.xml で構成された Bean は、子アプリケーション コンテキストによってのみ表示されます。ルート アプリケーション コンテキストからアクセスする場合は、オプション 2 を使用するか、構成ファイルを再編成します。

2) ルート アプリケーション コンテキスト (現在の構成) にすべての Bean をロードし、ディスパッチャー サーブレットに空の構成パラメーターを追加します。

<servlet>
    <servlet-name>myapp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value></param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
于 2013-09-05T22:38:33.937 に答える
0

セルジの答えは正しいです。完全を期すために、ここに私の作業中のweb.xmlがあります:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee             http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">

    <servlet>
        <servlet-name>myapp</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value></param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>myapp</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

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

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/myapp-servlet.xml,/WEB-INF/myapp-security.xml</param-value>
    </context-param>

    <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>

于 2013-09-06T20:59:15.163 に答える