java - tutorial - Configurar Spring Security 3.x para tener múltiples puntos de entrada
spring security rest token authentication (4)
He estado utilizando Spring Security 3.x para manejar la autenticación de usuario para mis proyectos, y hasta ahora, ha funcionado perfectamente.
Recientemente recibí los requisitos para un nuevo proyecto. En este proyecto, requiere 2 juegos de autenticación de usuario: uno para autenticar a los empleados contra LDAP y otro para autenticar al cliente contra la base de datos. Estoy un poco perplejo sobre cómo configurar eso en Spring Security.
Mi idea inicial fue crear una pantalla de inicio de sesión que tenga los siguientes campos:
- botón de selección de campo - para que los usuarios elijan si son empleados o clientes.
-
j_username
user field. -
j_password
contraseña campo.
Si el usuario selecciona "empleado", quiero que Spring Security los autentique contra LDAP; de lo contrario, la credencial se autenticará contra la base de datos. Sin embargo, el problema es que el formulario se enviará a /j_spring_security_check
y no hay forma de que envíe el campo del botón de /j_spring_security_check
a mi proveedor de autenticación personalizado implementado. Mi idea inicial es que probablemente necesite dos URL de envío de formularios en lugar de confiar en la /j_spring_security_check
predeterminada. Cada URL será manejada por diferentes proveedores de autenticación, pero no estoy seguro de cómo configurar eso en Spring Security.
Sé que en Spring Security puedo configurar la autenticación fallida, por ejemplo, si falla la autenticación LDAP, se recurrirá a la autenticación de bases de datos, pero esto no es lo que estoy buscando en este nuevo proyecto.
¿Alguien puede compartir cómo exactamente debo configurar esto en Spring Security 3.x?
Gracias.
ACTUALIZACIÓN - 28/01/2011 - La técnica de @ EasyAngel
Estoy tratando de hacer lo siguiente:
- El formulario de ingreso del empleado se envía a
/j_spring_security_check_for_employee
- El formulario de cliente ingresa a
/j_spring_security_check_for_customer
La razón por la que deseo 2 inicios de sesión de formularios diferentes es permitirme manejar la autenticación de manera diferente en función del usuario, en lugar de realizar una autenticación de recuperación. Es posible que el empleado y el cliente tengan la misma identificación de usuario, en mi caso.
Incorporé la idea de @ EasyAngel, pero tengo que reemplazar algunas clases en desuso. El problema al que me enfrento actualmente es que ni los procesos de filtrado URL parecen estar registrados en Spring Security porque sigo obteniendo el Error 404: SRVE0190E: File not found: /j_spring_security_check_for_employee
. Mi instinto me springSecurityFilterChain
bean springSecurityFilterChain
no está bien conectado, por lo tanto, mis filtros personalizados no se utilizan en absoluto.
Por cierto, estoy usando WebSphere y tengo com.ibm.ws.webcontainer.invokefilterscompatibility=true
propiedad com.ibm.ws.webcontainer.invokefilterscompatibility=true
establecida en el servidor. Puedo presionar /j_spring_security_check
sin problema.
Aquí está mi configuración de seguridad completa: -
<?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>
Estoy comenzando una recompensa aquí porque parece que ya no puedo hacer funcionar esto por varios días ... la palabra es frustración. Espero que alguien señale los problemas, o si puede mostrarme una forma mejor o más limpia de manejar esto (en el código).
Estoy usando Spring Security 3.x.
Gracias.
ACTUALIZACIÓN 29/01/2011 - La técnica de @ Ritesh
De acuerdo, logré que el enfoque de @ Ritesh funcionara muy de cerca con lo que quería. Tengo el botón de radio que permite al usuario seleccionar si son clientes o empleados. Parece que este enfoque funciona bastante bien, con un problema ...
- Si el empleado inicia sesión con la credencial correcta, se le permite ... TRABAJAR COMO SE ESPERABA .
- Si el empleado inicia sesión con una credencial incorrecta, no se les permite ... TRABAJAR COMO SE ESPERABA .
- Si el cliente inicia sesión con la credencial correcta, se le permite ... TRABAJAR COMO SE ESPERABA .
- Si el cliente inicia sesión con una credencial incorrecta, la autenticación vuelve a la autenticación del empleado ... NO FUNCIONA . Esto es arriesgado porque si selecciono la autenticación del cliente y le pongo la credencial del empleado, también permitirá al usuario entrar y esto no es lo que quiero.
<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>
Aquí está mi configuración actualizada. Tiene que ser una pequeña modificación que deba hacer para evitar que la autenticación retroceda pero parece que no puedo resolverlo ahora.
Gracias.
ACTUALIZACIÓN - SOLUCIÓN a la técnica de @ Ritesh
De acuerdo, creo que he resuelto el problema aquí. En lugar de tener EmployeeCustomAuthenticationProvider
para confiar en el UsernamePasswordAuthenticationToken
predeterminado, creé EmployeeUsernamePasswordAuthenticationToken
para él, al igual que el que creé CustomerUsernamePasswordAuthenticationToken
para CustomerCustomAuthenticationProvider
. Estos proveedores anularán los supports()
:
Clase CustomerCustomAuthenticationProvider
@Override
public boolean supports(Class<? extends Object> authentication) {
return (CustomerUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
Clase EmployeeCustomAuthenticationProvider
@Override
public boolean supports(Class<? extends Object> authentication) {
return (EmployeeUsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
Clase 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);
}
... y WALAA! ¡Funciona perfectamente ahora después de varios días de frustración!
Con suerte, este post podrá ayudar a alguien que esté haciendo lo mismo que yo aquí.
No es necesario crear /j_spring_security_check_for_employee
y /j_security_check_for_customer
filterProcessingUrl
.
El predeterminado funcionará perfectamente con la idea de campo del botón de opción.
En el inicio de sesión personalizado de LoginFilter
, debe crear diferentes tokens para empleado y cliente.
Estos son los pasos:
Use el nombre de
UsernamePasswordAuthenticationToken
predeterminadoUsernamePasswordAuthenticationToken
para el inicio de sesión del empleado.Crear
CustomerAuthenticationToken
para iniciar sesión como cliente. ExtiendaAbstractAuthenticationToken
para que su tipo de clase sea distinto deUsernamePasswordAuthenticationToken
.Definir un filtro de inicio de sesión personalizado:
<security:http> <security:custom-filter position="FORM_LOGIN_FILTER" ref="customFormLoginFilter" /> </security:http>
En
customFormLoginFilter
, anuleattemptAuthentication
siguiente manera (pseudo código):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); }
La anulación
supports
método enEmployeeCustomAuthenticationProvider
para admitirUsernamePasswordAuthenticationToken
.La anulación
supports
método enCustomerCustomAuthenticationProvider
para admitirCustomerAuthenticationToken
.@Override public boolean supports(Class<?> authentication) { return (CustomerAuthenticationToken.class.isAssignableFrom(authentication)); }
Use ambos proveedores en
authentication-manager
:<security:authentication-manager alias="authenticationManager"> <security:authentication-provider ref=''employeeCustomAuthenticationProvider '' /> <security:authentication-provider ref=''customerCustomAuthenticationProvider '' /> </security:authentication-manager>
Puede almacenar esta información en DB. Por ejemplo, puede tener una columna llamada ldap_auth
en la tabla Users
. Puedes ver mi otra respuesta (como un ejemplo):
Ejemplo de formulario de inicio de sesión Spring
Si observa cuidadosamente la clase UserService
, notará que realmente UserService
esta bandera LDAP y tomo la contraseña de usuario de LDAP o de la base de datos.
Puede definir varios filtros AuthenticationProcessingFilter
. Cada uno de ellos puede tener una URL diferente como / j_security_check_for_employee y / j_security_check_for_customer . Aquí hay un ejemplo del contexto de la aplicación de seguridad que demuestra esta idea:
<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>
Como puede ver, en este escenario también tiene diferentes UserDetailService
s - para DB auth y LDAP.
Creo que es una buena idea tener diferentes URL de autenticación para clientes y empleados (especialmente si usan diferentes estrategias de autenticación). Incluso puede tener diferentes páginas de inicio de sesión para ellos.
soy yo otra vez :) ¿Puedes tratar de usar filtros como este?
<sec:http auto-config="true">
...
<sec:custom-filter ref="authenticationProcessingFilterForCustomer" after="FIRST"/>
<sec:custom-filter ref="authenticationProcessingFilterForEmployee" after="FIRST"/>
</sec:http>
en lugar de definir bean springSecurityFilterChain
.