configurationSpring Security 3.x有多个入口点

我一直在使用Spring Security 3.x来处理我的项目的用户身份validation,到目前为止,它已经完美运行。

我最近收到了一个新项目的要求。 在这个项目中,需要两套用户authentication:一种是针对LDAPauthentication员工,另一种是针对数据库authentication客户。 我有点难以理解如何在Spring Security中进行configuration。

我最初的想法是创build一个有以下领域的login屏幕: –

  • 单选button字段 – 供用户select是员工还是客户。
  • j_username用户字段。
  • j_password密码字段。

如果用户select“员工”,那么我希望Spring Security根据LDAP对他们进行身份validation,否则证书将根据数据库进行身份validation。 但是,问题是表单将被提交给/j_spring_security_check并且我无法将单选button字段发送给我实现的自定义身份validation提供程序。 我最初的想法是我可能需要两个表单提交url,而不是依靠默认的/j_spring_security_check 。 每个URL将由不同的身份validation提供程序处理,但是我不确定如何在Spring Security中configuration它。

我知道在Spring Security中,我可以configuration回退身份validation,例如,如果LDAP身份validation失败,那么它将回退到数据库身份validation,但这不是我在这个新项目中拍摄的内容。

有人可以分享如何在Spring Security 3.x中configuration它吗?

谢谢。


更新 – 01-28-2011 – @ EasyAngel的技术

我正在努力做到以下几点:

  • 员工表单login提交给/j_spring_security_check_for_employee
  • 客户表单login提交给/j_spring_security_check_for_customer

我想要2种不同的表单login的原因是允许我根据用户不同的方式处理authentication,而不是做回退authentication。 就我而言,员工和客户可能有相同的用户ID。

我并入了@ EasyAngel的想法,但必须replace一些弃用的类。 我目前面临的问题是没有筛选进程URLS似乎注册在Spring安全,因为我不断收到Error 404: SRVE0190E: File not found: /j_spring_security_check_for_employee 。 我的直觉是springSecurityFilterChain bean没有正确连线,因此我的自定义filter根本没有使用。

顺便说一下,我正在使用WebSphere,并且在服务器中设置了com.ibm.ws.webcontainer.invokefilterscompatibility=true属性。 我能够毫无问题地打到默认的/j_spring_security_check

这是我完整的安全configuration:

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

我在这里开始赏金,因为我似乎已经好几天没有得到这个工作了……这个词是令人沮丧的。 我希望有人会指出这个问题,或者如果你能给我一个更好或更清洁的方式来处理这个(在代码中)。

我正在使用Spring Security 3.x.

谢谢。


更新01-29-2011 – @ Ritesh的技术

好吧,我设法让@ Ritesh的方法非常紧密地工作,以我想要的。 我有单选button,允许用户select他们是客户还是员工。 看来这种方法工作得很好,有一个问题…

  • 如果员工使用正确的凭证login,则允许他们进入…按预期工作
  • 如果员工使用错误凭证login,则不允许他们进入…按预期工作
  • 如果客户使用正确的凭证login,则允许他们进入…按预期工作
  • 如果客户使用错误的凭据login,则authentication会退回到员工身份validation… 不起作用 。 这是有风险的,因为如果我select了客户authentication,并将其打入员工证书,它也将允许用户,而这不是我想要的。
  <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> 

这是我更新的configuration。 我需要做一些非常小的调整,以防止身份validation倒退,但现在我似乎无法弄清楚。

谢谢。

更新 – 解决scheme@ Ritesh的技术

好的,我想我已经解决了这个问题。 而不是让EmployeeCustomAuthenticationProvider依赖于默认的UsernamePasswordAuthenticationToken ,我为它创build了EmployeeUsernamePasswordAuthenticationToken ,就像为CustomerCustomAuthenticationProvider创buildCustomerCustomAuthenticationProvider 。 这些提供程序将覆盖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); } 

…和WALAA! 它经过几天的挫折后,现在完美的工作!

希望这篇文章能够帮助那些和我在这里一样的人。

您不需要创build/j_security_check_for_customer/j_security_check_for_customer filterProcessingUrl

默认的一个将工作得很好,单选button领域的想法。

在自定义loginLoginFilter ,您需要为员工和客户创build不同的令牌。

这里是步骤:

  1. 使用默认的UsernamePasswordAuthenticationToken进行员工login。

  2. 为客户login创buildCustomerAuthenticationToken 。 扩展AbstractAuthenticationToken ,使其类types与UsernamePasswordAuthenticationToken不同。

  3. 定义一个自定义loginfilter:

     <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. Override supports EmployeeCustomAuthenticationProvider方法来支持UsernamePasswordAuthenticationToken

  6. Override supports CustomerCustomAuthenticationProvider方法来支持CustomerAuthenticationToken

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

您可以定义几个AuthenticationProcessingFilterfilter。 它们中的每一个都可以具有不同的URL,如/ j_security_check_for_employee/ j_security_check_for_customer 。 下面是安全应用程序上下文的例子,它演示了这个想法:

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

正如你所看到的,在这种情况下,你也有不同的UserDetailService – 用于数据库身份validation和LDAP。

我认为为客户和员工提供不同的身份validationURL是一个好主意(特别是如果他们使用不同的身份validation策略)。 你甚至可以有不同的login页面。

您可以将此信息存储在数据库中。 例如,你可以在Users表中有一个名为ldap_auth列。 你可以看看我的其他答案(作为一个例子):

Springlogin表单示例

如果你仔细看看UserService类,你会注意到,我实际上testing了这个LDAP标志,并从LDAP或数据库中获取用户密码。

这是我再次:)你可以尝试使用这样的filter:

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

而不是定义bean springSecurityFilterChain