gpt4 book ai didi

spring - Spring Security Filter Chain 如何工作

转载 作者:行者123 更新时间:2023-12-03 04:39:30 25 4
gpt4 key购买 nike

我意识到 Spring security 建立在过滤器链上,它将拦截请求,检测(缺少)身份验证,重定向到身份验证入口点或将请求传递给授权服务,并最终让请求命中 servlet 或抛出安全异常(未经身份验证或未经授权)。 DelegatingFitlerProxy 将这些过滤器粘合在一起。为了执行它们的任务,这些过滤器访问服务,例如 UserDetailsS​​ervice 和 AuthenticationManager。

链中的关键过滤器是(按顺序)

  • SecurityContextPersistenceFilter(从 JSESSIONID 恢复身份验证)
  • UsernamePasswordAuthenticationFilter(执行身份验证)
  • ExceptionTranslationFilter(从 FilterSecurityInterceptor 捕获安全异常)
  • FilterSecurityInterceptor(可能会抛出身份验证和授权异常)

  • 我很困惑如何使用这些过滤器。是不是spring提供的form-login,UsernamePasswordAuthenticationFilter只用于/login,后面的filter不是? form-login 命名空间元素是否会自动配置这些过滤器?每个请求(是否经过身份验证)是否都会到达 FilterSecurityInterceptor 以获取非登录 url?

    如果我想使用从登录中检索的 JWT token 来保护我的 REST API,该怎么办? 我必须配置两个命名空间配置 http标签,权利?一个用于/login 使用 UsernamePasswordAuthenticationFilter ,另一个用于 REST url,自定义 JwtAuthenticationFilter .

    是否配置两个 http元素创建两个 springSecurityFitlerChains ?是 UsernamePasswordAuthenticationFilter默认关闭,直到我声明 form-login ?如何更换 SecurityContextPersistenceFilter使用过滤器将获得 Authentication从现有 JWT-token而不是 JSESSIONID ?

    最佳答案

    Spring 安全过滤器链是一个非常复杂和灵活的引擎。

    Key filters in the chain are (in the order)

    • SecurityContextPersistenceFilter (restores Authentication from JSESSIONID)
    • UsernamePasswordAuthenticationFilter (performs authentication)
    • ExceptionTranslationFilter (catch security exceptions from FilterSecurityInterceptor)
    • FilterSecurityInterceptor (may throw authentication and authorization exceptions)


    看着 current stable release 4.2.1 documentation , 节 13.3 Filter Ordering你可以看到整个过滤器链的过滤器组织:

    13.3 Filter Ordering

    The order that filters are defined in the chain is very important. Irrespective of which filters you are actually using, the order should be as follows:

    1. ChannelProcessingFilter, because it might need to redirect to a different protocol

    2. SecurityContextPersistenceFilter, so a SecurityContext can be set up in the SecurityContextHolder at the beginning of a web request, and any changes to the SecurityContext can be copied to the HttpSession when the web request ends (ready for use with the next web request)

    3. ConcurrentSessionFilter, because it uses the SecurityContextHolder functionality and needs to update the SessionRegistry to reflect ongoing requests from the principal

    4. Authentication processing mechanisms - UsernamePasswordAuthenticationFilter, CasAuthenticationFilter, BasicAuthenticationFilter etc - so that the SecurityContextHolder can be modified to contain a valid Authentication request token

    5. The SecurityContextHolderAwareRequestFilter, if you are using it to install a Spring Security aware HttpServletRequestWrapper into your servlet container

    6. The JaasApiIntegrationFilter, if a JaasAuthenticationToken is in the SecurityContextHolder this will process the FilterChain as the Subject in the JaasAuthenticationToken

    7. RememberMeAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder, and the request presents a cookie that enables remember-me services to take place, a suitable remembered Authentication object will be put there

    8. AnonymousAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder, an anonymous Authentication object will be put there

    9. ExceptionTranslationFilter, to catch any Spring Security exceptions so that either an HTTP error response can be returned or an appropriate AuthenticationEntryPoint can be launched

    10. FilterSecurityInterceptor, to protect web URIs and raise exceptions when access is denied



    现在,我将尝试一一回答您的问题:

    I'm confused how these filters are used. Is it that for the spring provided form-login, UsernamePasswordAuthenticationFilter is only used for /login, and latter filters are not? Does the form-login namespace element auto-configure these filters? Does every request (authenticated or not) reach FilterSecurityInterceptor for non-login url?



    一旦您配置了 <security-http>部分,对于每一项,您必须至少提供一种身份验证机制。这必须是与我刚刚引用的 Spring Security 文档中的 13.3 过滤器排序部分中的组 4 匹配的过滤器之一。

    这是可以配置的最小有效 security:http 元素:
    <security:http authentication-manager-ref="mainAuthenticationManager" 
    entry-point-ref="serviceAccessDeniedHandler">
    <security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
    </security:http>

    就这么干吧,在过滤器链代理中配置了这些过滤器:
    {
    "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"
    }

    注意:我通过创建一个简单的 RestController 来获取它们,它 @Autowires FilterChainProxy 并返回它的内容:
        @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;
    }

    在这里,我们可以通过声明 <security:http> 来看到这一点。具有一个最低配置的元素,包括所有默认过滤器,但它们都不是身份验证类型(13.3 过滤器排序部分中的第 4 组)。所以它实际上意味着只需声明 security:http元素、SecurityContextPersistenceFilter、ExceptionTranslationFilter 和 FilterSecurityInterceptor 是自动配置的。

    实际上,应该配置一种认证处理机制,甚至安全命名空间bean 也为此处理声明,在启动时抛出错误,但可以绕过在 <http:security> 中添加entry-point-ref 属性。

    如果我添加一个基本的 <form-login>到配置,这样:
    <security:http authentication-manager-ref="mainAuthenticationManager">
    <security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
    <security:form-login />
    </security:http>

    现在,filterChain 将是这样的:
    {
    "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"
    }

    现在,这两个过滤器 org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter和 org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter 在 FilterChainProxy 中创建和配置。

    所以,现在,问题:

    Is it that for the spring provided form-login, UsernamePasswordAuthenticationFilter is only used for /login, and latter filters are not?



    是的,它用于在请求与 UsernamePasswordAuthenticationFilter url 匹配的情况下尝试完成登录处理机制。可以配置甚至更改此 url 的行为以匹配每个请求。

    您也可以在同一个 FilterchainProxy 中配置不止一种身份验证处理机制(例如 HttpBasic、CAS 等)。

    Does the form-login namespace element auto-configure these filters?



    不,表单登录元素配置 UsernamePasswordAUthenticationFilter,如果您不提供登录页面 url,它还配置 org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter,它以简单的自动生成登录结束页。

    其他过滤器默认自动配置,只需创建 <security:http>没有 security:"none" 的元素属性。

    Does every request (authenticated or not) reach FilterSecurityInterceptor for non-login url?



    每个请求都应该到达它,因为它是负责处理请求是否有权到达请求的 url 的元素。但是之前处理的一些过滤器可能会停止过滤器链处理,只是不调用 FilterChain.doFilter(request, response); .例如,如果请求没有 csrf 参数,CSRF 过滤器可能会停止过滤器链处理。

    What if I want to secure my REST API with JWT-token, which is retrieved from login? I must configure two namespace configuration http tags, rights? Other one for /login with UsernamePasswordAuthenticationFilter, and another one for REST url's, with custom JwtAuthenticationFilter.



    不,您不是被迫这样做的。您可以同时声明 UsernamePasswordAuthenticationFilterJwtAuthenticationFilter在同一个 http 元素中,但这取决于每个过滤器的具体行为。这两种方法都是可能的,最终选择哪一种取决于自己的喜好。

    Does configuring two http elements create two springSecurityFitlerChains?



    是的,这是真的

    Is UsernamePasswordAuthenticationFilter turned off by default, until I declare form-login?



    是的,您可以在我发布的每个配置中提出的过滤器中看到它

    How do I replace SecurityContextPersistenceFilter with one, which will obtain Authentication from existing JWT-token rather than JSESSIONID?



    您可以避免 SecurityContextPersistenceFilter,只需配置 session strategy<http:element> .只需像这样配置:
    <security:http create-session="stateless" >
    或者,在这种情况下,您可以用另一个过滤器覆盖它,这样在 <security:http> 中元素:
    <security:http ...>  
    <security:custom-filter ref="myCustomFilter" position="SECURITY_CONTEXT_FILTER"/>
    </security:http>
    <beans:bean id="myCustomFilter" class="com.xyz.myFilter" />

    编辑:

    One question about "You could too have more than one Authentication processing mechanisms configured in the same FilterchainProxy". Will the latter overwrite the authentication performed by first one, if declaring multiple (Spring implementation) authentication filters? How this relates to having multiple authentication providers?



    这最终取决于每个过滤器本身的实现,但事实是,后面的身份验证过滤器至少能够覆盖由前面的过滤器最终完成的任何先前的身份验证。

    但这不一定会发生。我在安全 REST 服务中有一些生产案例,其中我使用了一种授权 token ,该 token 可以作为 Http header 或在请求正文中提供。因此,我配置了两个过滤器来恢复该 token ,一种是从 Http Header 中恢复,另一种是从自己的 rest 请求的请求正文中恢复。确实,如果一个 http 请求将身份验证 token 作为 Http header 和请求正文内部提供,则两个过滤器都将尝试执行将其委托(delegate)给管理器的身份验证机制,但只需检查请求是否正确就可以轻松避免已经在 doFilter() 开始时通过了身份验证每个过滤器的方法。

    拥有多个身份验证过滤器与拥有多个身份验证提供程序有关,但不要强求。在我之前公开的情况下,我有两个身份验证过滤器,但我只有一个身份验证提供程序,因为这两个过滤器创建了相同类型的身份验证对象,因此在这两种情况下,身份验证管理器都将其委托(delegate)给同一个提供程序。

    与此相反,我也有一个场景,我只发布一个 UsernamePasswordAuthenticationFilter 但用户凭据都可以包含在 DB 或 LDAP 中,所以我有两个 UsernamePasswordAuthenticationToken 支持提供程序,并且 AuthenticationManager 将来自过滤器的任何身份验证尝试委托(delegate)给提供程序依次验证凭据。

    所以,我认为很明显,身份验证过滤器的数量既不能决定身份验证提供者的数量,也不能决定过滤器的数量。

    Also, documentation states SecurityContextPersistenceFilter is responsible of cleaning the SecurityContext, which is important due thread pooling. If I omit it or provide custom implementation, I have to implement the cleaning manually, right? Are there more similar gotcha's when customizing the chain?



    我之前没有仔细研究过这个过滤器,但是在你最后一个问题之后,我一直在检查它的实现,而且通常在 Spring 中,几乎所有东西都可以配置、扩展或覆盖。

    SecurityContextPersistenceFilter代表在 SecurityContextRepository实现对 SecurityContext 的搜索。默认情况下,一个 HttpSessionSecurityContextRepository使用,但这可以使用过滤器的构造函数之一进行更改。因此,最好编写一个适合您需求的 SecurityContextRepository 并在 SecurityContextPersistenceFilter 中对其进行配置,相信它已被证明的行为,而不是从头开始制作所有内容。

    关于spring - Spring Security Filter Chain 如何工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41480102/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com