web-dev-qa-db-ja.com

複数のエントリポイントを持つようにSpring Security 3.xを構成する

私は自分のプロジェクトのユーザー認証を処理するためにSpring Security 3.xを使用してきましたが、これまでのところ完璧に機能しました。

最近、新しいプロジェクトの要件を受け取りました。このプロジェクトでは、2セットのユーザー認証が必要です。1つはLDAPに対して従業員を認証し、もう1つはデータベースに対して顧客を認証します。 Spring Securityでそれを構成する方法に少し困惑しています。

私の最初のアイデアは、次のフィールドを持つログイン画面を作成することでした:

  • ラジオボタンフィールド-ユーザーが従業員か顧客かを選択します。
  • _j_username_ユーザーフィールド。
  • _j_password_パスワードフィールド。

ユーザーが「従業員」を選択した場合、Spring SecurityがLDAPに対してそれらを認証するようにします。そうしないと、資格情報がデータベースに対して認証されます。ただし、問題はフォームが_/j_spring_security_check_に送信されることであり、実装されたカスタム認証プロバイダーにラジオボタンフィールドを送信する方法がありません。私の最初の考えは、デフォルトの_/j_spring_security_check_に依存するのではなく、おそらく2つのフォーム送信URLが必要だと思います。各URLは異なる認証プロバイダーによって処理されますが、Spring Securityでどのように構成するのかわかりません。

Spring Securityでは、フォールバック認証を構成できます。たとえば、LDAP認証が失敗した場合、データベース認証にフォールバックしますが、これはこの新しいプロジェクトで狙っているものではありません。

Spring Security 3.xでこれをどのように設定する必要があるかを誰かが共有できますか?

ありがとうございました。


更新-2011年1月28日-@EasyAngelのテクニック

私は次のことをしようとしています:-

  • 従業員フォームのログインは_/j_spring_security_check_for_employee_に送信します
  • 顧客フォームのログインは_/j_spring_security_check_for_customer_に送信します

2つの異なるフォームログインが必要な理由は、フォールバック認証を行う代わりに、ユーザーに基づいて異なる方法で認証を処理できるようにするためです。私の場合、従業員と顧客が同じユーザーIDを持っている可能性があります。

@EasyAngelのアイデアを取り入れましたが、非推奨のクラスを置き換える必要があります。現在直面している問題は、_Error 404: SRVE0190E: File not found: /j_spring_security_check_for_employee_を取得し続けるため、どちらのフィルタープロセスもURLセキュリティがSpring Securityに登録されていないように見えることです。私の直感では、springSecurityFilterChain Beanが正しく配線されていないため、カスタムフィルターはまったく使用されていません。

ところで、私はWebSphereを使用していますが、サーバーに_com.ibm.ws.webcontainer.invokefilterscompatibility=true_プロパティが設定されています。デフォルトで_/j_spring_security_check_を問題なくヒットできます。

ここに私の完全なセキュリティ構成があります:-

_<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:sec="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

    <sec:http auto-config="true">
        <sec:form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" default-target-url="/welcome.jsp"
            always-use-default-target="true" />
        <sec:logout logout-success-url="/login.jsp" />
        <sec:intercept-url pattern="/employee/**" access="ROLE_EMPLOYEE" />
        <sec:intercept-url pattern="/customer/**" access="ROLE_CUSTOMER" />
        <sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
    </sec:http>

    <bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
        <sec:filter-chain-map path-type="ant">
            <sec:filter-chain pattern="/**" filters="authenticationProcessingFilterForEmployee, authenticationProcessingFilterForCustomer" />
        </sec:filter-chain-map>
    </bean>

    <bean id="authenticationProcessingFilterForEmployee" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManagerForEmployee" />
        <property name="filterProcessesUrl" value="/j_spring_security_check_for_employee" />
    </bean>

    <bean id="authenticationProcessingFilterForCustomer" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManagerForCustomer" />
        <property name="filterProcessesUrl" value="/j_spring_security_check_for_customer" />
    </bean>

    <bean id="authenticationManagerForEmployee" class="org.springframework.security.authentication.ProviderManager">
        <property name="providers">
            <list>
                <ref bean="employeeCustomAuthenticationProvider" />
            </list>
        </property>
    </bean>

    <bean id="authenticationManagerForCustomer" class="org.springframework.security.authentication.ProviderManager">
        <property name="providers">
            <list>
                <ref bean="customerCustomAuthenticationProvider" />
            </list>
        </property>
    </bean>

    <bean id="employeeCustomAuthenticationProvider" class="ss.EmployeeCustomAuthenticationProvider">
        <property name="userDetailsService">
            <bean class="ss.EmployeeUserDetailsService"/>
        </property>
    </bean>

    <bean id="customerCustomAuthenticationProvider" class="ss.CustomerCustomAuthenticationProvider">
        <property name="userDetailsService">
            <bean class="ss.CustomerUserDetailsService"/>
        </property>
    </bean>

    <sec:authentication-manager>
        <sec:authentication-provider ref="employeeCustomAuthenticationProvider" />
        <sec:authentication-provider ref="customerCustomAuthenticationProvider" />
    </sec:authentication-manager>

</beans>
_

すでに数日間これを機能させることができないように思えるので、私はここで賞金を始めています...フラストレーションはWordです。私は誰かが問題を指摘するか、またはあなたがこれを(コードで)処理するより良いまたはよりきれいな方法を私に示すことができるかどうかを望んでいます。

Spring Security 3.xを使用しています。

ありがとうございました。


アップデート01-29-2011-@Riteshのテクニック

さて、私は望んでいたものと非常に密接に連携する@Riteshのアプローチを得ることができました。ユーザーが顧客か従業員かを選択できるラジオボタンがあります。このアプローチは、1つの問題があり、かなりうまく機能しているようです...

  • 従業員が正しい資格情報でログインしている場合、ログインが許可されます... WORK AS EXPECTED
  • 従業員が間違った資格情報でログインした場合、ログインは許可されません... WORK AS EXPECTED
  • 顧客が正しい資格情報でログインした場合、ログインが許可されます... WORK AS EXPECTED
  • 顧客が間違った資格情報でログインすると、認証は従業員認証にフォールバックします... DOES N'T WORK。顧客認証を選択し、従業員の資格情報を入力すると、ユーザーも許可されるため、これは危険です。これは私が望むものではありません。
_    <sec:http auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint">
        <sec:logout logout-success-url="/login.jsp"/>
        <sec:intercept-url pattern="/employee/**" access="ROLE_EMPLOYEE"/>
        <sec:intercept-url pattern="/customer/**" access="ROLE_CUSTOMER"/>
        <sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>

        <sec:custom-filter position="FORM_LOGIN_FILTER" ref="myAuthenticationFilter"/>
    </sec:http>


    <bean id="myAuthenticationFilter" class="ss.MyAuthenticationFilter">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="authenticationFailureHandler" ref="failureHandler"/>
        <property name="authenticationSuccessHandler" ref="successHandler"/>
    </bean>

    <bean id="loginUrlAuthenticationEntryPoint"
          class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
        <property name="loginFormUrl" value="/login.jsp"/>
    </bean>

    <bean id="successHandler"
          class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
        <property name="defaultTargetUrl" value="/welcome.jsp"/>
        <property name="alwaysUseDefaultTargetUrl" value="true"/>
    </bean>

    <bean id="failureHandler"
          class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
        <property name="defaultFailureUrl" value="/login.jsp?login_error=1"/>
    </bean>


    <bean id="employeeCustomAuthenticationProvider" class="ss.EmployeeCustomAuthenticationProvider">
        <property name="userDetailsService">
            <bean class="ss.EmployeeUserDetailsService"/>
        </property>
    </bean>

    <bean id="customerCustomAuthenticationProvider" class="ss.CustomerCustomAuthenticationProvider">
        <property name="userDetailsService">
            <bean class="ss.CustomerUserDetailsService"/>
        </property>
    </bean>


    <sec:authentication-manager alias="authenticationManager">
        <sec:authentication-provider ref="customerCustomAuthenticationProvider"/>
        <sec:authentication-provider ref="employeeCustomAuthenticationProvider"/>
    </sec:authentication-manager>
</beans>
_

これが私の更新された構成です。認証のフォールバックを防止するために必要な、本当に小さな調整である必要がありますが、今ではそれを把握できないようです。

ありがとうございました。

更新-@Riteshの手法に対する解決策

さて、ここで問題を解決したと思います。 EmployeeCustomAuthenticationProviderをデフォルトのUsernamePasswordAuthenticationTokenに依存させる代わりに、EmployeeUsernamePasswordAuthenticationTokenCustomerUsernamePasswordAuthenticationTokenを作成したように、CustomerCustomAuthenticationProviderを作成しました。 _。これらのプロバイダーはsupports():-をオーバーライドします

CustomerCustomAuthenticationProviderクラス

_@Override
public boolean supports(Class<? extends Object> authentication) {
    return (CustomerUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
_

EmployeeCustomAuthenticationProviderクラス

_@Override
public boolean supports(Class<? extends Object> authentication) {
    return (EmployeeUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
_

MyAuthenticationFilterクラス

_public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

    ...

    UsernamePasswordAuthenticationToken authRequest = null;

    if ("customer".equals(request.getParameter("radioAuthenticationType"))) {
        authRequest = new CustomerUsernamePasswordAuthenticationToken(username, password);

    }
    else {
        authRequest = new EmployeeUsernamePasswordAuthenticationToken(username, password);
    }

    setDetails(request, authRequest);

    return super.getAuthenticationManager().authenticate(authRequest);
}
_

...そしてワラア!数日間のフラストレーションの後、完全に機能するようになりました!

うまくいけば、この投稿が、私がここにいるのと同じことをしている人を助けることができることを願っています。

62
limc

/j_spring_security_check_for_employeeおよび/j_security_check_for_customerfilterProcessingUrlを作成する必要はありません。

デフォルトは、ラジオボタンフィールドのアイデアでうまく機能します。

カスタムログインLoginFilterで、従業員と顧客に異なるトークンを作成する必要があります。

手順は次のとおりです。

  1. 従業員のログインにはデフォルトのUsernamePasswordAuthenticationTokenを使用します。

  2. 顧客ログイン用にCustomerAuthenticationTokenを作成します。クラス型がAbstractAuthenticationTokenと異なるようにUsernamePasswordAuthenticationTokenを拡張します。

  3. カスタムログインフィルタを定義します。

    <security:http>
        <security:custom-filter position="FORM_LOGIN_FILTER" ref="customFormLoginFilter" />
    </security:http>
    
  4. customFormLoginFilterで、attemptAuthenticationを次のようにオーバーライドします(擬似コード):

    if (radiobutton_param value employee) {
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        setDetails(whatever);
        return getAuthenticationManager().authenticate(authRequest);
    } else if (radiobutton_param value customer) {
        CustomerAuthenticationToken authRequest = new CustomerAuthenticationToken(username, password);
        setDetails(whatever);
        return getAuthenticationManager().authenticate(authRequest);
    }
    
  5. supportsをサポートするには、EmployeeCustomAuthenticationProviderUsernamePasswordAuthenticationTokenメソッドをオーバーライドします。

  6. supportsをサポートするには、CustomerCustomAuthenticationProviderCustomerAuthenticationTokenメソッドをオーバーライドします。

    @Override
    public boolean supports(Class<?> authentication) {
        return (CustomerAuthenticationToken.class.isAssignableFrom(authentication));
    }
    
  7. authentication-managerで両方のプロバイダーを使用します。

    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider ref='employeeCustomAuthenticationProvider ' />
        <security:authentication-provider ref='customerCustomAuthenticationProvider ' />
    </security:authentication-manager>
    
25
Ritesh

いくつかのAuthenticationProcessingFilterフィルターを定義できます。それぞれに/ j_security_check_for_employee/ j_security_check_for_customerのような異なるURLを設定できます。このアイデアを示すセキュリティアプリケーションコンテキストの例を次に示します。

<bean id="myfilterChainProxy" class="org.springframework.security.util.FilterChainProxy">
     <security:filter-chain-map pathType="ant">
         <security:filter-chain pattern="/**" filters="authenticationProcessingFilterForCustomer, authenticationProcessingFilterForEmployee, ..." />
     </security:filter-chain-map>
</bean>


<bean id="authenticationProcessingFilterForCustomer" class="org.springframework.security.web.authentication.AuthenticationProcessingFilter">
    <property name="authenticationManager" ref="authenticationManagerForCustomer"/>
    <property name="filterProcessesUrl" value="/j_security_check_for_customer"/>
</bean>

<bean id="authenticationProcessingFilterForEmployee" class="org.springframework.security.web.authentication.AuthenticationProcessingFilter">
    <property name="authenticationManager" ref="authenticationManagerForEmployee"/>
    <property name="filterProcessesUrl" value="/j_security_check_for_employee"/>
</bean>

<bean id="authenticationManagerForCustomer" class="org.springframework.security.authentication.ProviderManager">
    <property name="providers">
        <list>
            <bean class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
                <property name="userDetailsService">
                    <ref bean="customerUserDetailsServiceThatUsesDB"/>
                </property>
            </bean>
        </list>
    </property>
</bean>

<bean id="authenticationManagerForEmployee" class="org.springframework.security.authentication.ProviderManager">
    <property name="providers">
        <list>
            <bean class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
                <property name="userDetailsService">
                    <ref bean="employeeUserDetailsServiceThatUsesLDAP"/>
                </property>
            </bean>
        </list>
    </property>
</bean>

ご覧のとおり、このシナリオでは、DB認証とLDAP用に異なるUserDetailServicesもあります。

顧客と従業員に異なる認証URLを使用することをお勧めします(特に、異なる認証戦略を使用する場合)。異なるログインページを使用することもできます。

5
tenshi

再び私です:)このようなフィルターを使用してみてください:

<sec:http auto-config="true">
    ...
    <sec:custom-filter ref="authenticationProcessingFilterForCustomer" after="FIRST"/>
    <sec:custom-filter ref="authenticationProcessingFilterForEmployee" after="FIRST"/>
</sec:http>

bean springSecurityFilterChainを定義する代わりに。

0
tenshi

この情報をDBに保存できます。たとえば、Usersテーブルにldap_authという列を作成できます。私の他の答えを見ることができます(例として):

春のログインフォームの例

UserServiceクラスを注意深く見ると、このLDAPフラグを実際にテストし、LDAPまたはデータベースからユーザーパスワードを取得していることに気付くでしょう。

0
tenshi