Configurando o Spring Security 3.x para ter vários pontos de entrada
Eu tenho usado o Spring Security 3.x para manipular a autenticação do usuário para meus projetos e, até agora, funcionou perfeitamente.
Recentemente, recebi os requisitos para um novo projeto. Neste projeto, são necessários 2 conjuntos de autenticação de usuário: um para autenticar funcionários no LDAP e outro para autenticar o cliente no banco de dados. Estou um pouco confuso sobre como configurar isso no Spring Security.
Minha ideia inicial foi criar uma tela de login com os seguintes campos: -
campo do botão de opção - para que os usuários escolham se são funcionários ou clientes.j_username
Campo do usuário.j_password
campo de senha.Se o usuário selecionar "empregado", desejo que o Spring Security os autentique no LDAP, caso contrário, a credencial será autenticada no banco de dados. No entanto, o problema é que o formulário será enviado para/j_spring_security_check
e não há como enviar o campo do botão de opção ao meu provedor de autenticação personalizado implementado. Meu pensamento inicial é que provavelmente preciso de dois URLs de envio de formulários, em vez de confiar no padrão/j_spring_security_check
. Cada URL será tratada por diferentes provedores de autenticação, mas não sei como configurá-lo no Spring Security.
Eu sei que no Spring Security, posso configurar a autenticação de fallback, por exemplo, se a autenticação LDAP falhar, ela voltará para a autenticação do banco de dados, mas não é para isso que estou procurando neste novo projeto.
Alguém pode compartilhar como exatamente eu devo configurar isso no Spring Security 3.x?
Obrigado.
ATUALIZAÇÃO - 28-01-2011 - técnica do @ EasyAngel
Estou tentando fazer o seguinte: -
O login do formulário do funcionário envia para/j_spring_security_check_for_employee
O login do formulário do cliente envia para/j_spring_security_check_for_customer
A razão pela qual eu quero dois logins de formulário diferentes é permitir que eu manipule a autenticação de maneira diferente com base no usuário, em vez de fazer uma autenticação de fallback. É possível que funcionário e cliente tenham o mesmo ID de usuário, no meu caso.
Incorporei a ideia do @ EasyAngel, mas tenho que substituir algumas classes obsoletas. O problema que estou enfrentando atualmente não é que os URLs dos processos de filtro parecem registrados no Spring Security porque eu continuo recebendoError 404: SRVE0190E: File not found: /j_spring_security_check_for_employee
. Meu pressentimento é ospringSecurityFilterChain
bean não está conectado corretamente, portanto, meus filtros personalizados não são usados.
A propósito, estou usando o WebSphere e tenhocom.ibm.ws.webcontainer.invokefilterscompatibility=true
propriedade definida no servidor. Eu sou capaz de acertar o padrão/j_spring_security_check
sem problema.
Aqui está minha configuração completa de segurança: -
<?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>
Estou começando uma recompensa aqui porque parece que não consigo fazer isso funcionar há vários dias ... frustração é a palavra. Espero que alguém aponte o (s) problema (s) ou se você pode me mostrar uma maneira melhor ou mais limpa de lidar com isso (no código).
Estou usando o Spring Security 3.x.
Obrigado.
ATUALIZAÇÃO 29-01-2011 - técnica de @ Ritesh
Ok, consegui fazer com que a abordagem de @ Ritesh trabalhasse muito de perto do que eu queria. Eu tenho o botão de opção que permite ao usuário selecionar se é cliente ou funcionário. Parece que essa abordagem está funcionando muito bem, com um problema ...
Se o funcionário fizer logon com a credencial correta, ele poderá entrar em ...TRABALHO COMO ESPERADO.Se o funcionário efetuar login com credencial incorreta, ele não será permitido em ...TRABALHO COMO ESPERADO.Se o cliente efetuar login com credencial correta, ele será permitido em ...TRABALHO COMO ESPERADO.Se o cliente efetuar login com credencial incorreta, a autenticação voltará à autenticação do funcionário ...NÃO FUNCIONA. Isso é arriscado, porque se eu selecionar a autenticação do cliente e perfurar a credencial do funcionário, também permitirá que o usuário entre, e não é isso que eu quero. <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>
Aqui está minha configuração atualizada. Tem que haver alguns ajustes realmente pequenos que eu preciso fazer para impedir que a autenticação caia, mas não consigo entender agora.
Obrigado.
UPDATE - SOLUÇÃO para a técnica de @ Ritesh
Ok, acho que resolvi o problema aqui. Em vez de terEmployeeCustomAuthenticationProvider
confiar no padrãoUsernamePasswordAuthenticationToken
, Eu crieiEmployeeUsernamePasswordAuthenticationToken
por isso, assim como o que eu crieiCustomerUsernamePasswordAuthenticationToken
paraCustomerCustomAuthenticationProvider
. Esses provedores substituirão osupports()
: -
Classe CustomerCustomAuthenticationProvider
@Override
public boolean supports(Class<? extends Object> authentication) {
return (CustomerUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
Classe EmployeeCustomAuthenticationProvider
@Override
public boolean supports(Class<? extends Object> authentication) {
return (EmployeeUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
Classe 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);
}
... e WALAA! Funciona perfeitamente agora após vários dias de frustração!
Felizmente, este post poderá ajudar alguém que está fazendo a mesma coisa que eu estou aqui.