Saltar al contenido

Cómo funciona la cadena de filtro de seguridad Spring

Solución:

La cadena de filtros de seguridad Spring es un motor muy complejo y flexible.

Los filtros clave en la cadena son (en el orden)

  • SecurityContextPersistenceFilter (restaura la autenticación de JSESSIONID)
  • UsernamePasswordAuthenticationFilter (realiza la autenticación)
  • ExceptionTranslationFilter (captura excepciones de seguridad de FilterSecurityInterceptor)
  • FilterSecurityInterceptor (puede generar excepciones de autenticación y autorización)

Si observa la documentación de la versión estable actual 4.2.1, sección 13.3 Orden de filtros, puede ver la organización de filtros de toda la cadena de filtros:

13.3 Pedido de filtros

El orden en que se definen los filtros en la cadena es muy importante. Independientemente de los filtros que esté utilizando, el orden debe ser el siguiente:

  1. ChannelProcessingFilter, porque es posible que deba redirigir a un protocolo diferente

  2. SecurityContextPersistenceFilter, por lo que se puede configurar un SecurityContext en SecurityContextHolder al comienzo de una solicitud web, y cualquier cambio en SecurityContext se puede copiar en HttpSession cuando finaliza la solicitud web (listo para usar con la siguiente solicitud web)

  3. ConcurrentSessionFilter, porque utiliza la funcionalidad SecurityContextHolder y necesita actualizar SessionRegistry para reflejar las solicitudes en curso del principal

  4. Mecanismos de procesamiento de autenticación –
    Nombre de usuarioPasswordAuthenticationFilter, CasAuthenticationFilter,
    Filtro de autenticación básico etc – para que SecurityContextHolder pueda modificarse para contener un token de solicitud de autenticación válido

  5. los SecurityContextHolderAwareRequestFilter, si lo está utilizando para instalar un HttpServletRequestWrapper compatible con Spring Security en su contenedor de servlets

  6. los JaasApiIntegrationFilter, si un JaasAuthenticationToken está en SecurityContextHolder, esto procesará FilterChain como Asunto en JaasAuthenticationToken

  7. RecuérdameAuthenticationFilter, de modo que si ningún mecanismo de procesamiento de autenticación anterior actualizó SecurityContextHolder, y la solicitud presenta una cookie que habilita los servicios de recordarme, se colocará allí un objeto de autenticación recordado adecuado

  8. AnonymousAuthenticationFilter, de modo que si ningún mecanismo de procesamiento de autenticación anterior actualizó SecurityContextHolder, se colocará allí un objeto de autenticación anónimo

  9. ExceptionTranslationFilter, para detectar cualquier excepción de Spring Security para que se pueda devolver una respuesta de error HTTP o se pueda iniciar un AuthenticationEntryPoint apropiado

  10. FilterSecurityInterceptor, para proteger los URI web y generar excepciones cuando se deniega el acceso

Ahora, intentaré continuar con sus preguntas una por una:

Estoy confundido sobre cómo se usan estos filtros. ¿Es que para el formulario de inicio de sesión proporcionado por Spring, UsernamePasswordAuthenticationFilter solo se usa para / login, y los últimos filtros no? ¿El elemento de espacio de nombres de inicio de sesión de formulario configura automáticamente estos filtros? ¿Cada solicitud (autenticada o no) llega a FilterSecurityInterceptor para la URL sin inicio de sesión?

Una vez que esté configurando un <security-http> sección, para cada uno debe proporcionar al menos un mecanismo de autenticación. Este debe ser uno de los filtros que coincida con el grupo 4 en la sección 13.3 Orden de filtros de la documentación de Spring Security que acabo de mencionar.

Esta es la seguridad mínima válida: elemento http que se puede configurar:

<security:http authentication-manager-ref="mainAuthenticationManager" 
               entry-point-ref="serviceAccessDeniedHandler">
    <security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
</security:http>

Simplemente haciéndolo, estos filtros se configuran en el proxy de la cadena de filtros:

{
        "1": "org.springframework.security.web.context.SecurityContextPersistenceFilter",
        "2": "org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter",
        "3": "org.springframework.security.web.header.HeaderWriterFilter",
        "4": "org.springframework.security.web.csrf.CsrfFilter",
        "5": "org.springframework.security.web.savedrequest.RequestCacheAwareFilter",
        "6": "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter",
        "7": "org.springframework.security.web.authentication.AnonymousAuthenticationFilter",
        "8": "org.springframework.security.web.session.SessionManagementFilter",
        "9": "org.springframework.security.web.access.ExceptionTranslationFilter",
        "10": "org.springframework.security.web.access.intercept.FilterSecurityInterceptor"
    }

Nota: los obtengo creando un RestController simple que @Autowires el FilterChainProxy y devuelve su contenido:

    @Autowired
    private FilterChainProxy filterChainProxy;

    @Override
    @RequestMapping("/filterChain")
    public @ResponseBody Map<Integer, Map<Integer, String>> getSecurityFilterChainProxy(){
        return this.getSecurityFilterChainProxy();
    }

    public Map<Integer, Map<Integer, String>> getSecurityFilterChainProxy(){
        Map<Integer, Map<Integer, String>> filterChains= new HashMap<Integer, Map<Integer, String>>();
        int i = 1;
        for(SecurityFilterChain secfc :  this.filterChainProxy.getFilterChains()){
            //filters.put(i++, secfc.getClass().getName());
            Map<Integer, String> filters = new HashMap<Integer, String>();
            int j = 1;
            for(Filter filter : secfc.getFilters()){
                filters.put(j++, filter.getClass().getName());
            }
            filterChains.put(i++, filters);
        }
        return filterChains;
    }

Aquí pudimos ver que simplemente declarando el <security:http> elemento con una configuración mínima, se incluyen todos los filtros predeterminados, pero ninguno de ellos es de tipo Autenticación (cuarto grupo en la sección 13.3 Orden de filtros). Por lo que en realidad significa que con solo declarar el security:http , el SecurityContextPersistenceFilter, el ExceptionTranslationFilter y el FilterSecurityInterceptor se configuran automáticamente.

De hecho, se debe configurar un mecanismo de procesamiento de autenticación, e incluso los beans de espacio de nombres de seguridad procesan reclamos para eso, arrojando un error durante el inicio, pero se puede omitir agregando un atributo de referencia de punto de entrada en <http:security>

Si agrego un básico <form-login> a la configuración, de esta manera:

<security:http authentication-manager-ref="mainAuthenticationManager">
    <security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
    <security:form-login />
</security:http>

Ahora, el filterChain será así:

{
        "1": "org.springframework.security.web.context.SecurityContextPersistenceFilter",
        "2": "org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter",
        "3": "org.springframework.security.web.header.HeaderWriterFilter",
        "4": "org.springframework.security.web.csrf.CsrfFilter",
        "5": "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter",
        "6": "org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter",
        "7": "org.springframework.security.web.savedrequest.RequestCacheAwareFilter",
        "8": "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter",
        "9": "org.springframework.security.web.authentication.AnonymousAuthenticationFilter",
        "10": "org.springframework.security.web.session.SessionManagementFilter",
        "11": "org.springframework.security.web.access.ExceptionTranslationFilter",
        "12": "org.springframework.security.web.access.intercept.FilterSecurityInterceptor"
    }

Ahora, estos dos filtros org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter y org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter se crean y configuran en FilterChainProxy.

Entonces, ahora, las preguntas:

¿Es que para el formulario de inicio de sesión proporcionado por Spring, UsernamePasswordAuthenticationFilter solo se usa para / login, y los últimos filtros no?

Sí, se usa para intentar completar un mecanismo de procesamiento de inicio de sesión en caso de que la solicitud coincida con la URL de UsernamePasswordAuthenticationFilter. Esta URL se puede configurar o incluso cambiar su comportamiento para que coincida con cada solicitud.

También podría tener más de un mecanismo de procesamiento de autenticación configurado en el mismo FilterchainProxy (como HttpBasic, CAS, etc.).

¿El elemento de espacio de nombres de inicio de sesión de formulario configura automáticamente estos filtros?

No, el elemento form-login configura UsernamePasswordAUthenticationFilter y, en caso de que no proporcione una URL de página de inicio de sesión, también configura org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter, que termina en un inicio de sesión simple generado automáticamente página.

Los otros filtros se configuran automáticamente de forma predeterminada con solo crear un <security:http> elemento sin security:"none" atributo.

¿Cada solicitud (autenticada o no) llega a FilterSecurityInterceptor para la URL que no es de inicio de sesión?

Toda solicitud debe llegar a ella, ya que es el elemento que se encarga de si la solicitud tiene los derechos para llegar a la url solicitada. Pero algunos de los filtros procesados ​​antes podrían detener el procesamiento de la cadena de filtros simplemente sin llamar FilterChain.doFilter(request, response);. Por ejemplo, un filtro CSRF podría detener el procesamiento de la cadena de filtros si la solicitud no tiene el parámetro csrf.

¿Qué sucede si quiero proteger mi API REST con el token JWT, que se recupera del inicio de sesión? Debo configurar dos etiquetas http de configuración de espacio de nombres, ¿derechos? Otro para / iniciar sesión con UsernamePasswordAuthenticationFiltery otro para las URL de REST, con personalización JwtAuthenticationFilter.

No, no estás obligado a hacerlo de esta manera. Podrías declarar ambos UsernamePasswordAuthenticationFilter y el JwtAuthenticationFilter en el mismo elemento http, pero depende del comportamiento concreto de cada uno de estos filtros. Ambos enfoques son posibles, y cuál elegir finalmente depende de las propias preferencias.

¿La configuración de dos elementos http crea dos springSecurityFitlerChains?

Sí, eso es verdad

¿UsernamePasswordAuthenticationFilter está desactivado de forma predeterminada hasta que declare formulario-login?

Sí, lo podías ver en los filtros levantados en cada una de las configuraciones que publiqué

¿Cómo reemplazo SecurityContextPersistenceFilter con uno, que obtendrá la autenticación del token JWT existente en lugar de JSESSIONID?

Puede evitar SecurityContextPersistenceFilter, simplemente configurando la estrategia de sesión en <http:element>. Simplemente configure así:

<security:http create-session="stateless" >

O, en este caso, podría sobrescribirlo con otro filtro, de esta manera dentro del <security:http> elemento:

<security:http ...>  
   <security:custom-filter ref="myCustomFilter" position="SECURITY_CONTEXT_FILTER"/>    
</security:http>
<beans:bean id="myCustomFilter" class="com.xyz.myFilter" />

EDITAR:

Una pregunta sobre “Usted también podría tener más de un mecanismo de procesamiento de autenticación configurado en el mismo FilterchainProxy”. ¿Este último sobrescribirá la autenticación realizada por el primero, si declara varios filtros de autenticación (implementación de Spring)? ¿Cómo se relaciona esto con tener varios proveedores de autenticación?

Esto finalmente depende de la implementación de cada filtro en sí, pero es cierto el hecho de que los últimos filtros de autenticación al menos pueden sobrescribir cualquier autenticación previa eventualmente realizada por los filtros anteriores.

Pero esto no sucederá necesariamente. Tengo algunos casos de producción en servicios REST seguros en los que utilizo un tipo de token de autorización que se puede proporcionar como un encabezado Http o dentro del cuerpo de la solicitud. Entonces configuro dos filtros que recuperan ese token, en un caso del encabezado Http y el otro del cuerpo de la solicitud de la propia solicitud de descanso. Es cierto el hecho de que si una solicitud http proporciona ese token de autenticación tanto como encabezado Http como dentro del cuerpo de la solicitud, ambos filtros intentarán ejecutar el mecanismo de autenticación delegándolo al administrador, pero podría evitarse fácilmente simplemente verificando si la solicitud es ya autenticado justo al comienzo de la doFilter() método de cada filtro.

Tener más de un filtro de autenticación está relacionado con tener más de un proveedor de autenticación, pero no lo fuerce. En el caso que expuse antes, tengo dos filtros de autenticación pero solo tengo un proveedor de autenticación, ya que ambos filtros crean el mismo tipo de objeto de autenticación, por lo que en ambos casos el administrador de autenticación lo delega al mismo proveedor.

Y al contrario de esto, yo también tengo un escenario en el que publico solo un UsernamePasswordAuthenticationFilter pero las credenciales de usuario pueden estar contenidas en DB o LDAP, por lo que tengo dos proveedores de soporte de UsernamePasswordAuthenticationToken, y AuthenticationManager delega cualquier intento de autenticación del filtro a los proveedores secuencialmente para validar las credenciales.

Entonces, creo que está claro que ni la cantidad de filtros de autenticación determina la cantidad de proveedores de autenticación ni la cantidad de proveedores determina la cantidad de filtros.

Además, la documentación indica que SecurityContextPersistenceFilter es responsable de limpiar SecurityContext, que es importante debido a la agrupación de subprocesos. Si lo omito o proporciono una implementación personalizada, tengo que implementar la limpieza manualmente, ¿verdad? ¿Hay más trampas similares al personalizar la cadena?

No miré detenidamente este filtro antes, pero después de su última pregunta, he estado verificando su implementación y, como es habitual en Spring, casi todo podría configurarse, extenderse o sobrescribirse.

SecurityContextPersistenceFilter delega en una implementación de SecurityContextRepository la búsqueda de SecurityContext. Por defecto, se usa un HttpSessionSecurityContextRepository, pero esto podría cambiarse usando uno de los constructores del filtro. Por lo tanto, puede ser mejor escribir un SecurityContextRepository que se adapte a sus necesidades y simplemente configurarlo en SecurityContextPersistenceFilter, confiando en su comportamiento probado en lugar de comenzar a hacer todo desde cero.

Spring Security es un marco basado en filtros, planta un WALL (HttpFireWall) antes de su aplicación en términos de filtros proxy o beans administrados por Spring. Su solicitud debe pasar por varios filtros para llegar a su API.

Secuencia de ejecución en Spring Security

  1. WebAsyncManagerIntegrationFilter Proporciona integración entre SecurityContext y WebAsyncManager de Spring Web.

  2. SecurityContextPersistenceFilter Este filtro solo se ejecutará una vez por solicitud, llena el SecurityContextHolder con información obtenida de la configuración SecurityContextRepository antes de la solicitud y lo almacena nuevamente en el repositorio una vez que la solicitud se ha completado y borrando el contenedor de contexto.
    Se comprueba la solicitud para la sesión existente. Si la solicitud es nueva, se creará SecurityContext; de lo contrario, si la solicitud tiene sesión, el contexto de seguridad existente se obtendrá del repositorio.

  3. HeaderWriterFilter Implementación de filtros para agregar encabezados a la respuesta actual.

  4. LogoutFilter Si la URL de solicitud es /logout(para la configuración predeterminada) o si solicita url mathces RequestMatcher configurado en LogoutConfigurer luego

    • borra el contexto de seguridad.
    • invalida la sesión
    • elimina todas las cookies con nombres de cookies configurados en LogoutConfigurer
    • Redirige a la URL predeterminada de éxito de cierre de sesión / o url de éxito de cierre de sesión configurado o invoca logoutSuccessHandler configurado.
  5. UsernamePasswordAuthenticationFilter

    • Para cualquier URL de solicitud que no sea loginProcessingUrl, este filtro no procesará más, pero la cadena de filtros simplemente continúa.
    • Si la URL solicitada coincide (debe ser HTTP POST) defecto /login o partidos .loginProcessingUrl() configurado en FormLoginConfigurer luego UsernamePasswordAuthenticationFilter intenta la autenticación.
    • Los parámetros predeterminados del formulario de inicio de sesión son el nombre de usuario y la contraseña, pueden ser anulados por usernameParameter(String), passwordParameter(String).
    • configuración .loginPage() anula los valores predeterminados
    • Al intentar la autenticación
      • un Authentication objeto(UsernamePasswordAuthenticationToken o cualquier implementación de Authentication en el caso de su filtro de autenticación personalizado).
      • y authenticationManager.authenticate(authToken) será invocado
      • Tenga en cuenta que podemos configurar cualquier número de AuthenticationProvider El método de autenticación prueba todos los proveedores de autenticación y verifica cualquiera de los proveedores de autenticación. supports authToken / objeto de autenticación, el proveedor de autenticación de soporte se utilizará para la autenticación. y devuelve el objeto de autenticación en caso de que la autenticación se realice correctamente; de ​​lo contrario, arroja AuthenticationException.
    • Si se creará una sesión exitosa de autenticación y authenticationSuccessHandler se invocará, lo que redirige a la URL de destino configurada (el valor predeterminado es /)
    • Si la autenticación falla, el usuario se convierte en un usuario no autenticado y la cadena continúa.
  6. SecurityContextHolderAwareRequestFilter, si lo está utilizando para instalar un HttpServletRequestWrapper compatible con Spring Security en su contenedor de servlets

  7. AnonymousAuthenticationFilter Detecta si no hay ningún objeto de autenticación en SecurityContextHolder, si no se encuentra ningún objeto de autenticación, crea Authentication objetoAnonymousAuthenticationToken) con autoridad otorgada ROLE_ANONYMOUS. Aquí AnonymousAuthenticationToken Facilita la identificación de solicitudes posteriores de usuarios no autenticados.

Registros de depuración

DEBUG - /app/admin/app-config at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
DEBUG - Populated SecurityContextHolder with anonymous token: 'org.sprin[email protected]aeef7b36: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.sprin[email protected]b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS' 
  1. ExceptionTranslationFilter, para detectar cualquier excepción de Spring Security para que se pueda devolver una respuesta de error HTTP o se pueda iniciar un AuthenticationEntryPoint apropiado

  2. FilterSecurityInterceptor

    Habrá FilterSecurityInterceptor que es casi el último en la cadena de filtros que obtiene el objeto de autenticación de SecurityContext y obtiene la lista de autoridades otorgadas (roles otorgados) y tomará una decisión si permite que esta solicitud llegue al recurso solicitado o no, la decisión se toma al hacer coincidir con el permitido AntMatchers configurado en HttpSecurityConfiguration.

Considere las excepciones 401-No autorizado y 403-Prohibido. Estas decisiones se tomarán al final de la cadena de filtros.

  • Usuario no autenticado que intenta acceder a un recurso público – Permitido
  • Usuario no autenticado que intenta acceder a un recurso seguro – 401-No autorizado
  • Usuario autenticado que intenta acceder a un recurso restringido (restringido para su función) – 403-Prohibido

Nota: La solicitud de usuario fluye no solo en los filtros mencionados anteriormente, sino que también hay otros filtros que no se muestran aquí. (ConcurrentSessionFilter,RequestCacheAwareFilter,SessionManagementFilter …)
Será diferente cuando use su filtro de autenticación personalizado en lugar de UsernamePasswordAuthenticationFilter.
Será diferente si configura el filtro de autenticación JWT y omite .formLogin() i.e, UsernamePasswordAuthenticationFilter se convertirá en un caso completamente diferente.


Solo para referencia. Filtros en spring-web y spring-security
Nota: consulte el nombre del paquete en la imagen, ya que hay algunos otros filtros de orm y mi filtro personalizado implementado.

ingrese la descripción de la imagen aquí

De la documentación, el orden de los filtros se da como

  • ChannelProcessingFilter
  • ConcurrentSessionFilter
  • SecurityContextPersistenceFilter
  • LogoutFilter
  • X509 Filtro de autenticación
  • ResumenPreAuthenticatedProcessingFilter
  • CasAuthenticationFilter
  • Nombre de usuarioPasswordAuthenticationFilter
  • ConcurrentSessionFilter
  • OpenIDAuthenticationFilter
  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter
  • ConcurrentSessionFilter
  • DigestAuthenticationFilter
  • BearerTokenAuthenticationFilter
  • Filtro de autenticación básico
  • RequestCacheAwareFilter
  • SecurityContextHolderAwareRequestFilter
  • JaasApiIntegrationFilter
  • RecuérdameAuthenticationFilter
  • AnonymousAuthenticationFilter
  • SessionManagementFilter
  • ExceptionTranslationFilter
  • FilterSecurityInterceptor
  • SwitchUserFilter

También puedes referir
forma más común de autenticar una aplicación web moderna?
diferencia entre autenticación y autorización en el contexto de Spring Security?

UsernamePasswordAuthenticationFilter solo se usa para /loginy los últimos filtros no lo son?

No, UsernamePasswordAuthenticationFilter se extiende AbstractAuthenticationProcessingFilter, y esto contiene un RequestMatcher, eso significa que puede definir su propia URL de procesamiento, este filtro solo maneja el RequestMatcher coincide con la URL de la solicitud, la URL de procesamiento predeterminada es /login.

Los filtros posteriores aún pueden manejar la solicitud, si el UsernamePasswordAuthenticationFilter ejecuta chain.doFilter(request, response);.

Más detalles sobre los ajustadores de núcleos

¿El elemento de espacio de nombres de inicio de sesión de formulario configura automáticamente estos filtros?

UsernamePasswordAuthenticationFilter es creado por <form-login>, estos son los alias de filtro estándar y el orden

¿Cada solicitud (autenticada o no) llega a FilterSecurityInterceptor para la URL sin inicio de sesión?

Depende de si los anteriores fitlers tienen éxito, pero FilterSecurityInterceptor es el último instalador normalmente.

¿La configuración de dos elementos http crea dos springSecurityFitlerChains?

Sí, cada FitlerChain tiene un RequestMatcher, Si el RequestMatcher coincide con la solicitud, la solicitud será manejada por los montadores de la cadena de montadores.

El valor por defecto RequestMatcher coincide con todas las solicitudes si no configura el patrón, o puede configurar la URL específica (<http pattern="/rest/**").

Si quieres saber más sobre los fitlers, creo que puedes comprobar el código fuente en Spring Security.
doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *