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

lim*_*imc 64 java authentication spring forms-authentication spring-security

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

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

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

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

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

我知道在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_employee.我的直觉是springSecurityFilterChainbean没有正确连接,因此根本不使用我的自定义过滤器.

顺便说一下,我正在使用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>
Run Code Online (Sandbox Code Playgroud)

我在这里开始获得赏金,因为我似乎已经好几天都没有这个工作......沮丧就是这个词.我希望有人会指出问题,或者你是否可以告诉我一个更好或更清洁的方法来处理这个问题(在代码中).

我正在使用Spring Security 3.x.

谢谢.


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

好吧,我设法让@ Ritesh的方法与我想要的非常接近.我有radiobutton,允许用户选择他们是客户还是员工.看起来这种方法工作得相当好,有一个问题......

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

这是我更新的配置.它必须是我需要做的一些非常小的调整,以防止身份验证回落,但我现在似乎无法弄明白.

谢谢.

更新 - @ Ritesh技术的解决方案

好的,我想我已经解决了这个问题.相反,具有EmployeeCustomAuthenticationProvider依赖于默认的UsernamePasswordAuthenticationToken,我创建EmployeeUsernamePasswordAuthenticationToken了它,就像我创建了一个CustomerUsernamePasswordAuthenticationTokenCustomerCustomAuthenticationProvider.然后这些提供商将覆盖supports(): -

CustomerCustomAuthenticationProvider类

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

EmployeeCustomAuthenticationProvider类

@Override
public boolean supports(Class<? extends Object> authentication) {
    return (EmployeeUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

......还有WALAA!经过几天的挫折,它现在完美地运作了!

希望这篇文章能够帮助那些和我在一起做同样事情的人.

Rit*_*esh 27

你不需要创建/j_spring_security_check_for_employee/j_security_check_for_customer filterProcessingUrl.

默认的一个可以正常使用单选按钮字段的想法.

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

以下是步骤:

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

  2. 创建CustomerAuthenticationToken供客户登录.扩展AbstractAuthenticationToken以使其类类型与之不同UsernamePasswordAuthenticationToken.

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

    <security:http>
        <security:custom-filter position="FORM_LOGIN_FILTER" ref="customFormLoginFilter" />
    </security:http>
    
    Run Code Online (Sandbox Code Playgroud)
  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);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 覆盖supports方法EmployeeCustomAuthenticationProvider以支持UsernamePasswordAuthenticationToken.

  6. 覆盖supports方法CustomerCustomAuthenticationProvider以支持CustomerAuthenticationToken.

    @Override
    public boolean supports(Class<?> authentication) {
        return (CustomerAuthenticationToken.class.isAssignableFrom(authentication));
    }
    
    Run Code Online (Sandbox Code Playgroud)
  7. 使用两个提供商authentication-manager:

    <security:authentication-manager alias="authenticationManager">
        <security:authentication-provider ref='employeeCustomAuthenticationProvider ' />
        <security:authentication-provider ref='customerCustomAuthenticationProvider ' />
    </security:authentication-manager>
    
    Run Code Online (Sandbox Code Playgroud)


ten*_*shi 5

您可以定义几个AuthenticationProcessingFilter过滤器。它们每个都可以具有不同的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>
Run Code Online (Sandbox Code Playgroud)

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

我认为对客户和员工使用不同的身份验证URL是个好主意(尤其是当他们使用不同的身份验证策略时)。您甚至可以拥有不同的登录页面。