Spring Security 存在多个过滤器(Spring Security专题)

这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

写在前面

目前Spring Security 专题现在已经更新到17篇了。算上本篇文章已经有18篇了,感兴趣的小伙伴可以点击我头像查看专栏。我们一起学习,

多说一句,目前正在进行的是并发队列专栏和Security专栏,设计模式专栏已完结,感兴趣的伙伴可以去看下

今天我们来聊聊 Security当中的过滤器,看过我以前文章的朋友应该知道 Security的权限功能是通过过滤器来实现的。而且这个过滤器还不止一个,默认使用的是15个。而且全部好像有32个。具体等我研究完这些过滤器了 再给大家一一分享

过滤器

我们从过滤器开始。先来回顾下在一个 Web 项目中,请求流程是什么样的,如下图所示

image.png

没错吧 是这个样子,请求从客户端发起(例如浏览器),然后穿过层层 Filter,最终来到 Servlet 上,被 Servlet 所处理。

这会儿应该抛出一个问题 : Spring Security 中默认的 15 个过滤器就是这样嵌套在 Client 和 Servlet 之间吗?

答案肯定不是的! 我们分析下

上图中的 Filter 我们可以称之为 Web Filter,Spring Security 中的 Filter 我们可以称之为 Security Filter,它们之间的关系如下图:

image.png

可以看到,Spring Security Filter 并不是直接嵌入到 Web Filter 中的,而是通过 FilterChainProxy 来统一管理 Spring Security Filter,FilterChainProxy 本身则通过 Spring 提供的 DelegatingFilterProxy 代理过滤器嵌入到 Web Filter 之中。

多个过滤器链

上面和大家介绍的是单个过滤器链,实际上,在 Spring Security 中,可能存在多个过滤器链。

看下面这段代码配置是不是就是多个过滤器链?

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/admin/**").hasRole("admin")
            .antMatchers("/user/**").hasRole("user")
            .anyRequest().authenticated()
            ...
            .csrf().disable();
}
复制代码

这样的配置相信大家都见过,但是这并不是多个过滤器链,这是一个过滤器链。因为不管是 /admin/** 还是 /user/** ,走过的过滤器都是一样的,只是不同的路径判断条件不一样而已。

如果系统存在多个过滤器链,多个过滤器链会在 FilterChainProxy 中进行划分,如下图:

image.png

可以看到,当请求到达 FilterChainProxy 之后,FilterChainProxy 会根据请求的路径,将请求转发到不同的 Spring Security Filters 上面去,不同的 Spring Security Filters 对应了不同的过滤器,也就是不同的请求将经过不同的过滤器。

正常情况下,我们配置的都是一个过滤器链,多个过滤器链怎么配置呢?下面我给大家举一个简单的例子:

@Configuration
public class SecurityConfig {
    @Bean
    protected UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("shushi").password("{bcrypt}$2a$10$Sb1gAUH4wwazfNiqflKZve4Ubh.spJcxgHG8Cp29DeGya5zsHENqi").roles("admin", "aaa", "bbb").build());
        manager.createUser(User.withUsername("yn").password("{noop}123").roles("admin").build());
        manager.createUser(User.withUsername("一点东西").password("{MD5}{Wucj/L8wMTMzFi3oBKWsETNeXbMFaHZW9vCK9mahMHc=}4d43db282b36d7f0421498fdc693f2a2").roles("user", "aaa", "bbb").build());
        return manager;
    }

    @Configuration
    @Order(1)
    static class DefaultWebSecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/foo/**")
                    .authorizeRequests()
                    .anyRequest().hasRole("admin")
                    .and()
                    .csrf().disable();
        }
    }

    @Configuration
    @Order(2)
    static class DefaultWebSecurityConfig2 extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher("/bar/**")
                    .authorizeRequests()
                    .anyRequest().hasRole("user")
                    .and()
                    .formLogin()
                    .permitAll()
                    .and()
                    .csrf().disable();
        }
    }
}
复制代码

下面对上面代码一一分析下:

  • 首先,SecurityConfig 不再需要继承自 WebSecurityConfigurerAdapter 了,只是作为一个普通的配置类,加上 @Configuration 注解即可。

  • 提供 UserDetailsService 实例,相当于是我们的数据源。

  • 创建静态内部类继承 WebSecurityConfigurerAdapter 类,同时用 @Configuration 注解标记静态内部类是一个配置类,配置类里边的代码就和之前的一样了

  • 每一个静态内部类相当于就是一个过滤器链的配置

  • 注意在静态内部类里边,我没有使用 http.authorizeRequests() 开始,http.authorizeRequests() 配置表示该过滤器链过滤的路径是 /。在静态内部类里边,我是用了 http.antMatcher("/bar/") 开启配置,表示将当前过滤器链的拦截范围限定在 /bar/**。

  • 当存在多个过滤器链的时候,必然会有一个优先级的问题,所以每一个过滤器链的配置类上通过 @Order(2) 注解来标记优先级。

OK 关于Spring Security 的多个过滤器学习就到这里。我们下期再见 加油!

弦外之音

感谢你的阅读,如果你感觉学到了东西,您可以点赞,关注。也欢迎有问题我们下面评论交流

加油! 我们下期再见!

给大家分享几个我前面写的几篇骚操作

copy对象,这个操作有点骚!

干货!SpringBoot利用监听事件,实现异步操作

Guess you like

Origin juejin.im/post/7034465489892048903