将 Spring Security 3.x 配置为具有多个入口点

我一直在使用Spring Security 3.x来处理我的项目的用户身份验证,到目前为止,它一直完美无缺。

我最近收到了一个新项目的要求。在此项目中,它需要 2 组用户身份验证:一组用于根据 LDAP 对员工进行身份验证,另一组用于根据数据库对客户进行身份验证。我对如何在Spring Security中配置它感到有些困惑。

我最初的想法是创建一个具有以下字段的登录屏幕:-

  • 单选按钮字段 - 供用户选择他们是员工还是客户。
  • j_username用户字段。
  • j_password密码字段。

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

我知道在Spring Security中,我可以配置回退身份验证,例如,如果LDAP身份验证失败,那么它将回退到数据库身份验证,但这不是我在这个新项目中要做的。

有人可以分享我应该如何在Spring Security 3.x中配置它吗?

谢谢。


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

我正在尝试执行以下操作:-

  • 员工表单登录提交到/j_spring_security_check_for_employee
  • 客户表单登录提交至/j_spring_security_check_for_customer

我想要2个不同的表单登录的原因是允许我根据用户以不同的方式处理身份验证,而不是执行回退身份验证。在我的情况下,员工和客户可能具有相同的用户ID。

我纳入了@EasyAngel的想法,但必须替换一些已弃用的类。我目前面临的问题是两个过滤进程都没有URLS似乎在Spring Security中注册,因为我一直在得到.我的直觉是豆子没有正确接线,因此根本没有使用我的自定义过滤器。Error 404: SRVE0190E: File not found: /j_spring_security_check_for_employeespringSecurityFilterChain

顺便说一句,我正在使用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>

我在这里开始赏金,因为我似乎已经好几天无法让它工作了几天......挫折是这个词。我希望有人会指出问题,或者如果你能向我展示一种更好或更干净的方式来处理这个问题(在代码中)。

我正在使用Spring Security 3.x。

谢谢。


2011年01月29日 - @Ritesh技术更新

好吧,我设法@Ritesh的方法非常接近我想要的。我有单选按钮,允许用户选择他们是客户还是员工。似乎这种方法运行得相当不错,但有一个问题......

  • 如果员工使用正确的凭据登录,则允许他们进入...按预期工作
  • 如果员工使用错误的凭据登录,则不允许他们进入...按预期工作
  • 如果客户使用正确的凭据登录,则允许他们进入...按预期工作
  • 如果客户使用错误的凭据登录,则身份验证将回退到员工身份验证...不起作用。这是有风险的,因为如果我选择客户身份验证,并将其打入员工凭据,它也将允许用户进入,这不是我想要的。
    <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技术的解决方案

好吧,我想我已经解决了这里的问题。我不必依赖默认值 ,而是为它创建了,就像我为 .然后,这些提供程序将覆盖 :-EmployeeCustomAuthenticationProviderUsernamePasswordAuthenticationTokenEmployeeUsernamePasswordAuthenticationTokenCustomerUsernamePasswordAuthenticationTokenCustomerCustomAuthenticationProvidersupports()

客户自定义身份验证提供程序类

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

员工自定义身份验证提供程序类

@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);
}

...和瓦拉!经过几天的沮丧,它现在工作得很好!

希望这篇文章能够帮助那些正在做与我在这里相同事情的人。


答案 1

您不需要创建 和 。/j_spring_security_check_for_employee/j_security_check_for_customerfilterProcessingUrl

默认的一个将与单选按钮字段的想法一起工作得很好。

在 自定义登录 ,您需要为员工和客户创建不同的令牌。LoginFilter

步骤如下:

  1. 使用默认值进行员工登录。UsernamePasswordAuthenticationToken

  2. 为客户登录而创建。扩展,使其类类型不同于 。CustomerAuthenticationTokenAbstractAuthenticationTokenUsernamePasswordAuthenticationToken

  3. 定义自定义登录过滤器:

    <security:http>
        <security:custom-filter position="FORM_LOGIN_FILTER" ref="customFormLoginFilter" />
    </security:http>
    
  4. 在 中,覆盖如下(伪代码):customFormLoginFilterattemptAuthentication

    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. 重写 中的方法以支持 。supportsEmployeeCustomAuthenticationProviderUsernamePasswordAuthenticationToken

  6. 重写 中的方法以支持 。supportsCustomerCustomAuthenticationProviderCustomerAuthenticationToken

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

答案 2

您可以定义多个筛选器。它们中的每一个都可以有不同的URL,如/j_security_check_for_employee/j_security_check_for_customer。下面是演示此想法的安全应用程序上下文的示例:AuthenticationProcessingFilter

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

如您所见,在这种情况下,您也有不同的 s - 用于数据库身份验证和 LDAP。UserDetailService

我认为为客户和员工设置不同的身份验证 URL 是个好主意(特别是如果他们使用不同的身份验证策略)。您甚至可以为他们提供不同的登录页面。


推荐