Настройка Spring Security 3.x для нескольких точек входа

Я использую Spring Security 3.x для обработки аутентификации пользователей в моих проектах, и до сих пор она работала безупречно.

Недавно я получил требования для нового проекта. В этом проекте требуется 2 набора аутентификации пользователя: один для аутентификации сотрудников по LDAP, а другой для аутентификации клиента по базе данных. Я немного озадачен тем, как настроить это в Spring Security.

Моя первоначальная идея состояла в том, чтобы создать экран входа в систему, который имеет следующие поля:

  • поле переключателя - для пользователей, чтобы выбрать, являются ли они сотрудниками или клиентами.
  • j_username пользовательское поле.
  • j_password поле пароля.

Если пользователь выберет "сотрудник", я хочу, чтобы 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's техника

Я пытаюсь сделать следующее:-

  • Форма входа сотрудника /j_spring_security_check_for_employee
  • Форма входа для клиента /j_spring_security_check_for_customer

Причина, по которой я хочу иметь два разных входа в систему, заключается в том, что я могу по-разному обрабатывать аутентификацию в зависимости от пользователя, а не выполнять резервную аутентификацию. Возможно, у сотрудника и клиента одинаковый идентификатор пользователя, в моем случае.

Я включил идею @ EasyAngel, но должен заменить некоторые устаревшие классы. Проблема, с которой я сейчас сталкиваюсь, заключается в том, что ни один из процессов фильтрации не выглядит зарегистрированным в Spring Security, потому что я получаю Error 404: SRVE0190E: File not found: /j_spring_security_check_for_employee, Мое внутреннее чувство это springSecurityFilterChain Бин не подключен должным образом, поэтому мои пользовательские фильтры не используются вообще.

Кстати, я использую 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.

Спасибо.


ОБНОВЛЕНИЕ 01-29-2011 - @ Техника Ритеша

Хорошо, мне удалось заставить подход @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

Хорошо, я думаю, что я решил проблему здесь. Вместо того, чтобы иметь 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 class

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

... и ВАЛАА! Теперь он отлично работает после нескольких дней разочарования!

Надеюсь, этот пост сможет помочь кому-то, кто делает то же самое, что и я здесь.

4 ответа

Решение

Вам не нужно создавать /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. 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>
    

Вы можете определить несколько 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>

Как видите, в этом сценарии у вас также есть разные UserDetailServices - для аутентификации БД и LDAP.

Я думаю, что было бы неплохо иметь разные аутентификационные URL-адреса для клиентов и сотрудников (особенно если они используют разные стратегии аутентификации). Вы даже можете иметь разные страницы входа для них.

Для справки по конфигурации Java

Поскольку я очень хочу написать здесь способ конфигурации java для реализации того же метода, чтобы помочь людям, которые не знакомы с конфигурацией xml, но я не хочу похищать красоту этого потока с таким длинным ответом кода конфигурации java.

Люди, которые хотят добиться того же с конфигурацией java (на основе аннотаций), могут сослаться на мою ссылку на вопрос с ответом, приведенную ниже, а также вы можете найти ссылку на мой репозиторий github для кода в ответе.

Для кода конфигурации на основе аннотации см. Мой ответ здесь Несколько AuthenticationProvider с другим UsernamePasswordAuthToken для аутентификации различных форм входа без резервной аутентификации

Это снова я:) Можете ли вы попробовать использовать фильтры вот так:

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

вместо определения бобов springSecurityFilterChain,

Вы можете хранить эту информацию в БД. Например, вы можете иметь столбец с именем ldap_auth в Users Таблица. Вы можете посмотреть на мой другой ответ (в качестве примера):

Пример формы входа в Spring

Если вы внимательно посмотрите на UserService класс, вы заметите, что я на самом деле проверяю этот флаг LDAP и беру пароль пользователя из LDAP или базы данных.

Другие вопросы по тегам