私は自分のプロジェクトのユーザー認証を処理するために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つの問題があり、かなりうまく機能しているようです...
_ <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
に依存させる代わりに、EmployeeUsernamePasswordAuthenticationToken
にCustomerUsernamePasswordAuthenticationToken
を作成したように、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);
}
_
...そしてワラア!数日間のフラストレーションの後、完全に機能するようになりました!
うまくいけば、この投稿が、私がここにいるのと同じことをしている人を助けることができることを願っています。
/j_spring_security_check_for_employee
および/j_security_check_for_customer
filterProcessingUrl
を作成する必要はありません。
デフォルトは、ラジオボタンフィールドのアイデアでうまく機能します。
カスタムログインLoginFilter
で、従業員と顧客に異なるトークンを作成する必要があります。
手順は次のとおりです。
従業員のログインにはデフォルトのUsernamePasswordAuthenticationToken
を使用します。
顧客ログイン用にCustomerAuthenticationToken
を作成します。クラス型がAbstractAuthenticationToken
と異なるようにUsernamePasswordAuthenticationToken
を拡張します。
カスタムログインフィルタを定義します。
<security:http>
<security:custom-filter position="FORM_LOGIN_FILTER" ref="customFormLoginFilter" />
</security:http>
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);
}
supports
をサポートするには、EmployeeCustomAuthenticationProvider
のUsernamePasswordAuthenticationToken
メソッドをオーバーライドします。
supports
をサポートするには、CustomerCustomAuthenticationProvider
のCustomerAuthenticationToken
メソッドをオーバーライドします。
@Override
public boolean supports(Class<?> authentication) {
return (CustomerAuthenticationToken.class.isAssignableFrom(authentication));
}
authentication-manager
で両方のプロバイダーを使用します。
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref='employeeCustomAuthenticationProvider ' />
<security:authentication-provider ref='customerCustomAuthenticationProvider ' />
</security:authentication-manager>
いくつかの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用に異なるUserDetailService
sもあります。
顧客と従業員に異なる認証URLを使用することをお勧めします(特に、異なる認証戦略を使用する場合)。異なるログインページを使用することもできます。
再び私です:)このようなフィルターを使用してみてください:
<sec:http auto-config="true">
...
<sec:custom-filter ref="authenticationProcessingFilterForCustomer" after="FIRST"/>
<sec:custom-filter ref="authenticationProcessingFilterForEmployee" after="FIRST"/>
</sec:http>
bean springSecurityFilterChain
を定義する代わりに。
この情報をDBに保存できます。たとえば、Users
テーブルにldap_auth
という列を作成できます。私の他の答えを見ることができます(例として):
UserService
クラスを注意深く見ると、このLDAPフラグを実際にテストし、LDAPまたはデータベースからユーザーパスワードを取得していることに気付くでしょう。