Конфигурирование Spring Security 3.x, чтобы иметь несколько точек входа
Я использовал Spring Security 3.x для обработки аутентификации пользователей для моих проектов, и до сих пор он работал безупречно.
Недавно я получил требования к новому проекту. В этом проекте требуется два набора пользовательской аутентификации: один для проверки подлинности сотрудников по протоколу LDAP, а другой - для проверки подлинности клиента в отношении базы данных. Я немного озадачен тем, как настроить это в Spring безопасности.
Моя первоначальная идея заключалась в создании экрана входа в систему, который имеет следующие поля: -
- поле переключателя - для пользователей выбирать, являются ли они сотрудниками или клиентами.
-
j_username
поле пользователя.
-
j_password
поле пароля.
Если пользователь выбирает "employee", то я хочу, чтобы Spring Security проверила их на LDAP, иначе учетные данные будут аутентифицированы в отношении базы данных. Однако проблема заключается в том, что форма будет отправлена на /j_spring_security_check
, и мне не удастся отправить поле переключателя на мой реализованный пользовательский поставщик проверки подлинности. Моя первоначальная мысль заключается в том, что мне, вероятно, нужны два URL-адреса отправки, а не по умолчанию /j_spring_security_check
. Каждый URL-адрес будет обрабатываться различными поставщиками проверки подлинности, но я не уверен, как настроить его в Spring безопасности.
Я знаю в Spring безопасности, я могу настроить аутентификацию с возвратом, например, если аутентификация LDAP не удалась, тогда она вернется к аутентификации базы данных, но это не то, что я снимаю в этом новом проекте.
Может ли кто-то поделиться, как именно я должен настроить это в Spring безопасности 3.x?
Спасибо.
ОБНОВЛЕНИЕ - 01-28-2011 - Техника @EasyAngel
Я пытаюсь сделать следующее: -
- Вход для формы сотрудника отправляется в
/j_spring_security_check_for_employee
- Вход в форму клиента отправляется на
/j_spring_security_check_for_customer
Причина, по которой я хочу 2 разных входа в систему, - позволить мне обрабатывать аутентификацию по-разному на основе пользователя, вместо того, чтобы выполнять аутентификацию с возвратом. Возможно, у сотрудника и клиента одинаковый идентификатор пользователя, в моем случае.
Я включил идею @EasyAngel, но мне нужно заменить некоторые устаревшие классы. Проблема, с которой я сейчас сталкиваюсь, - это ни процессы фильтрации, ни URL-адреса, которые выглядят как URL-адреса в Spring безопасности, потому что я продолжаю получать Error 404: SRVE0190E: File not found: /j_spring_security_check_for_employee
. Чувство моего кишечника 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>
Я начинаю щедрость здесь, потому что я не могу заставить эту работу работать уже несколько дней... разочарование - это слово. Я надеюсь, кто-то укажет на проблему (ы), или если вы можете показать мне лучший или более чистый способ справиться с этим (в коде).
Я использую Spring Security 3.x.
Спасибо.
ОБНОВЛЕНИЕ 01-29-2011 - техника @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
Хорошо, думаю, я решил проблему здесь. Вместо того, чтобы 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);
}
... и WALAA! Он работает отлично после нескольких дней разочарования!
Надеюсь, этот пост сможет помочь кому-то, кто делает то же самое, что и я здесь.
Ответы
Ответ 1
Вам не нужно создавать /j_spring_security_check_for_employee
и /j_security_check_for_customer
filterProcessingUrl
.
По умолчанию все будет отлично работать с идеей поля переключателя.
В пользовательском login 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>
Ответ 2
Вы можете определить несколько фильтров 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>
Как вы можете видеть, в этом случае у вас также есть разные UserDetailService
- для DB auth и LDAP.
Я считаю хорошей идеей иметь разные URL-адреса авторизации для клиентов и сотрудников (особенно если они используют разные стратегии аутентификации). Вы можете даже иметь разные страницы входа для них.
Ответ 3
Ссылка на конфигурацию Java
Поскольку я стремился написать здесь, способ настройки java для реализации той же техники, чтобы помочь людям, которые не знакомы с конфигурацией xml, но я не хочу угонять красоту этого потока с таким длинным ответом кода конфигурации java.
Люди, которые хотят достичь того же с помощью конфигурации java (на основе аннотаций), могут ссылаться на мою ссылку для ответов на вопросы, приведенную ниже, а также вы можете найти мою ссылку на репозиторий github для кода в ответе.
Для получения кода конфигурации на основе аннотаций см. мой ответ здесь.
Несколько AuthenticationProvider с разными UsernamePasswordAuthToken для аутентификации различных форм входа без резервной аутентификации
Ответ 4
Вы можете сохранить эту информацию в БД. Например, вы можете иметь столбец с именем ldap_auth
в таблице Users
. Вы можете посмотреть мой другой ответ (в качестве примера):
Spring пример формы входа в систему
Если вы внимательно изучите класс UserService
, вы заметите, что я действительно проверяю этот флаг LDAP и беру пароль пользователя либо из LDAP, либо из базы данных.
Ответ 5
он мне снова:) Можете ли вы попробовать использовать такие фильтры:
<sec:http auto-config="true">
...
<sec:custom-filter ref="authenticationProcessingFilterForCustomer" after="FIRST"/>
<sec:custom-filter ref="authenticationProcessingFilterForEmployee" after="FIRST"/>
</sec:http>
вместо определения bean springSecurityFilterChain
.