1

Spring Security

一、POM依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.2.0 RELEASE<version>
</dependency>

二、 默认配置

添加依赖后,即可实现Spring Security的默认配置。
spring-boot-autoconfigurer项目下的WebSecurityEnablerConfiguration 自动加载Spring Security 默认配置[WebSecurityConfigurerAdapter]

Spring Security 默认配置主要如下:

  1. URL拦截(未登录,重定向登录页面)
  2. 登录页面 [GET]/login
  3. 登录接口 [POST]/login [from-data]username=xxx&password=xxx&_csrf=xxx
  4. 登出页面 [GET]/logout
  5. 登出接口 [POST]/logout
  6. 默认账号 username=user&password=xxx[在项目启动时会输出初始密码 Using generated security password:xxx]

三、过滤器链

如果你是第一次使用Spring Security,一定会觉得很cool!Spring Security是如何做到的。

Spring Security主要基于过滤器(责任链模式)和AOP实现。

过滤器的结构大致是这么个意思
image.png

那么FilterChainProxy是如何构建出来的?

Spring Security提供了两个接口

  1. SecurityBuilder<O> 泛型O代表构造器构建的对象类型
    1.1 build() 构建并返回O对象
  2. SecurityConfigurer<O,B extends SecurityBuilder<O>> 泛型B代表可配置的构造器类型,泛型O代表构造器构建的对象类型
    2.1 init(B builder) 初始化SecurityBuilder
    2.2 configure(B builder) 配置SecurityBuilder

Spring Security基于构造器模式,通过SecurityBuilder构造器构建对象,而SecurityConfigurer则负责对构造器进行初始化和配置。

FilterChainProxy通过WebSecurity构造器构建。

默认的WebSecurityWebSecurityConfiguration.setFilterChainProxySecurityConfigurer()完成初始化,
并从Spring容器中获取SecurityConfigurers<Filter, WebSecurity>列表实现对构造器的初始化和配置。

package org.springframework.security.config.annotation.web.configuration;

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
    private WebSecurity webSecurity;
    private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
    ...
    // 通过WebSecurity完成FilterChainProxy构建并注册到Spring容器
    @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public Filter springSecurityFilterChain() throws Exception {
        ...
        return webSecurity.build();
    }
    ...
    // 1 从spring容器中获取SecurityConfigurers<Filter, WebSecurity>配置列表
    // 2 创建WebSecurity
    // 3 将配置列表添加到WebSecurity
    @Autowired(required = false)
    public void setFilterChainProxySecurityConfigurer(
            ObjectPostProcessor<Object> objectPostProcessor,
            @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
            throws Exception {
        webSecurity = objectPostProcessor
                .postProcess(new WebSecurity(objectPostProcessor));
        ...
        for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
            webSecurity.apply(webSecurityConfigurer);
        }
        ...
    }
    ...
}

SecurityFilterChain通过HttpSecurity构造器构建。
默认的HttpSecurity在WebSecurityConfigurerAdapter.init()完成初始化,该方法是WebSecurity在构建FilterChainProxy的过程中触发,并最终完成SecurityFilterChain构建添加到FilterChainProxy中。

package org.springframework.security.config.annotation.web.configuration;

@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
        WebSecurityConfigurer<WebSecurity> {
    ...
    private HttpSecurity http;
    private boolean disableDefaults;
    ...
    // 初始化WebSecurity,在WebSecurity构建FilterChainProxy的过程中触发
    public void init(final WebSecurity web) throws Exception {
        final HttpSecurity http = getHttp();
        web.addSecurityFilterChainBuilder(http)...;
    }
    ...
    // 配置WebSecurity
    public void configure(WebSecurity web) {
    }
    ...       
    // 创建并返回HttpSecurity
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected final HttpSecurity getHttp() throws Exception {
        if (http != null) {
            return http;
        }
        ...
        http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
                sharedObjects);
        if (!disableDefaults) {
            http
                .csrf().and()
                .addFilter(new WebAsyncManagerIntegrationFilter())
                .exceptionHandling().and()
                .headers().and()
                .sessionManagement().and()
                .securityContext().and()
                .requestCache().and()
                .anonymous().and()
                .servletApi().and()
                .apply(new DefaultLoginPageConfigurer<>()).and()
                .logout();
            ClassLoader classLoader = this.context.getClassLoader();
            List<AbstractHttpConfigurer> defaultHttpConfigurers =
                    SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);

            for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
                http.apply(configurer);
            }
        }
        configure(http);
        return http;
    }
    ...
    // 配置HttpSecurity
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin().and()
            .httpBasic();
    }
    ...
}

下面则列出了一些"苦力"过滤器(默认过滤器列表)
当然,以下过滤器通常有对应的securityConfigurer完成构建。
构建逻辑在HttpSecurity的构建过程中,触发在其对应的SecurityConfigurer.configure(H http)中创建过滤器实例并添加到HttpSecurity中,最终添加到FilterChainProxy实例中。

1. WebAsyncManagerIntegrationFilter 支持集成Spring的异步调用
2. SecurityContextPersistenceFilter 从Session中获取SecurityContext,没有则新建,最终放入SecurityContextHolder中。
3. HeaderWriterFilter 往响应对象response中写入Header属性(Like X-Frame-Options, X-XSS-Protection and X- Content-Type-Options)
4. CsrfFilter csrf校验
5. LogoutFilter 默认拦截[POST]/logout 处理登出逻辑
6. UsernamePasswordAuthenticationFilter 默认拦截[POST]/login 处理登录认证逻辑
    6.1 第一步 封装Authentication(从request中获取认证信息封装为UsernamePasswordAuthenticationToken)
    6.2 第二步 获取AuthenticationManager实例[ProviderManager]
    6.3 第三步 ProviderManager基于委托者模式通过AuthenticationProvider列表完成认证
    6.4 第四步 认证通过后,将Authentication放入SecurityContextHolder的SecurityContext中。
7. DefaultLoginPageGeneratingFilter 默认拦截[GET]/login 生成登录页面
8. DefaultLogoutPageGeneratingFilter 默认拦截[POST]/login 生成登出页面 
9. BasicAuthenticationFilter 认证Basic [Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==]
10. RequestCacheAwareFilter 用于用户登录成功后,重新恢复因为登录被打断的请求
11. SecurityContextHolderAwareRequestFilter 包装request实现servlet api的一些接口方法isUserInRole、getRemoteUser
12. AnonymousAuthenticationFilter 匿名用户认证、信息填充
13. SessionManagementFilter Session管理
14. ExceptionTranslationFilter 异常处理
15. FilterSecurityInterceptor 权限校验、未登录拦截、无权限拦截
...

四、自定义配置


roylion
204 声望25 粉丝

读书破万卷


引用和评论

0 条评论