1.何为spring Security

Spring Security is a framework that provides authentication, authorization, and protection against common attacks. With first class support for both imperative and reactive applications, it is the de-facto standard for securing Spring-based applications.

Spring Security是一个提供认证,授权,保护应用应对常见的攻击的框架,对命令式和响应式的应用提供一流的支持,其是保护基于Spring应用的约定俗成的标准。

首先引用Spring Security官方文档的一段介绍文字,

该系列文章主要介绍Servlet应用中的Spring Security运用。

2.Spring Boot + spring Security 自动配置

  1. 开启Spring Security的自动配置后将创建一个名为springSecurityFilterChain的Filter,该bean负责应用中的所有安全职责(保护应用的路径,验证提交的账号秒,重定向到登录表单等)
  2. 创建一个UserDetailsService生成名为user,随机生成的密码(打印在控制台中)
  3. 在Servlet容器中注册名为springSecurityFilterChain的Filter来过滤所有的请求

3.Spring Boot + spring Security 简单的启动不需要很多的配置,但是做了很多事情

  1. 经过身份验证的用户才能与应用进行交互
  2. 生成一个默认的登录表单
  3. 在控制台生成用户密码供通过认证
  4. 使用BCrypt保护密码存储
  5. 提供注销接口
  6. 防止CSRF攻击
  7. 回话保护
  1. 强制安全传输技术
  2. X-Content-Type-Options头部集成
  3. 缓存控制
  4. XSS攻击防护(X-XSS-Protection)
  5. X-Frame-Options防止点击劫持
  1. 提供以下Servlet API方法
  1. getRemoteUser()
  2. isUserInRole(java.lang.String)
  3. isUserInRole(java.lang.String)
  4. login(java.lang.String, java.lang.String)
  5. logout()

4.Servlet Security 中的Filters

4.1 Filters一览

Spring Security 的Servlet支持是基于Servlet的Filters,如下图所示:

加上spring security 拦截器报错forbidden403_spring

客户端发送请求到应用,容器创建一个过滤链(FilterChain),其中包含Filters和Servlet,病根据请求URI的路径处理HttpServletRequest,在Spring MVC的应用中该Servlet的实例为DispatcherServlet,通常情况一个Servlet处理一个单独的HttpServletRequest和HttpServletResponse,但是,多个Filter可以这样使用:

  1. 防止下游的Filters或Servlet被调用。在当前处理的Filter中,过滤器可以直接写回HttpServletResponse
  2. 放过请求,由下游的Filters和Servlet来处理HttpServletRequest和HttpServletResponse

FilterChain使用例子:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    // 在此之前做些事情
    chain.doFilter(request, response); // 逻辑部分
    // 在此之后做些事情
}

4.2 DelegatingFilterProxy

Spring提供了一个Filter的实现类DelegatingFilterProxy,其在Servlet容器的生命周期和Spring的ApplicationContext之间建立桥梁,Servlet容器允许所以注册的Filters使用自己的标准,但是这些Filters无法获取到Spring定义的对象,DelegatingFilterProxy可以通过标准Servlet容器机制进行注册,但是将所有工作委托给实现了Filter的Spring对象。

加上spring security 拦截器报错forbidden403_java_02

DelegatingFilterProxy从ApplicationContext中获取到第一个Filter并且调用其过滤方法,以下是DelegatingFilterProxy伪代码:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
    // 惰性获取注册到Spring Bean的过滤器
    // 以下delegate为elegatingFilterProxy中的一个实例
    Filter delegate = getFilterBean(someBeanName);
    // 将工作委托给Spring Bean
    delegate.doFilter(request, response);
}

DelegatingFilterProxy的另一个好处是,它允许延迟查找Filterbean实例。这点很重要,因为容器需要Filter在容器启动之前注册实例。但是,Spring通常使用ContextLoaderListener来加载Spring Bean,直到Filter需要注册实例之后才能完成。使用DelegatingFilterProxy可以在调用的时候再去ApplicationContext获取需要的Filter

 

DelegatingFilterProxy由DelegatingFilterProxyRegistrationBean初始化

public DelegatingFilterProxy getFilter() {
        return new DelegatingFilterProxy(this.targetBeanName, this.getWebApplicationContext()) {
            protected void initFilterBean() throws ServletException {
            }
        };
    }
DelegatingFilterProxyRegistrationBean中targetBeanName的值为’springSecurityFilterChain‘

加上spring security 拦截器报错forbidden403_java_03

 

DelegatingFilterProxy中核心代码:

private String targetBeanName;
private volatile Filter delegate;

public DelegatingFilterProxy(String targetBeanName, @Nullable WebApplicationContext wac) {
        this.targetFilterLifecycle = false;
        this.delegateMonitor = new Object();
        Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty");
        this.setTargetBeanName(targetBeanName);
        this.webApplicationContext = wac;
        if (wac != null) {
            this.setEnvironment(wac.getEnvironment());
        }

    }


public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        Filter delegateToUse = this.delegate;
        if (delegateToUse == null) {
            synchronized(this.delegateMonitor) {
                delegateToUse = this.delegate;
                if (delegateToUse == null) {
                    WebApplicationContext wac = this.findWebApplicationContext();
                    if (wac == null) {
                        throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
                    }

                    delegateToUse = this.initDelegate(wac);
                }

                this.delegate = delegateToUse;
            }
        }

        this.invokeDelegate(delegateToUse, request, response, filterChain);
    }


    protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
        String targetBeanName = this.getTargetBeanName();
        Assert.state(targetBeanName != null, "No target bean name set");
        Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class);
        if (this.isTargetFilterLifecycle()) {
            delegate.init(this.getFilterConfig());
        }

        return delegate;
    }
  1. 首先DelegatingFilterProxyRegistrationBean新建DelegatingFilterProxy对象,并设置targetBeanName为springSecurityFilterChain
  2. 当执行到DelegatingFilterProxy中的doFilter方法时delegate对象为null
  3. 调用this.initDelegate(wac),从WebApplicationContext中获取到spring管理的delegate(springSecurityFilterChain)
  4. 调用springSecurityFilterChain的doFilter方法

4.3 FilterChainProxy

Spring Security的Servlet支持包含在FilterChainProxy中,FilterChainProxy是由Spring Security提供的一个特殊的Filter,其允许通过SecurityFilterChain委托多个过滤器实例,因为FilterChainProxy是一个Bean,所以它通常封装在DelegatingFilterProxy中。

加上spring security 拦截器报错forbidden403_表单_04

4.4 SecurityFilterChain

 

SecurityFilterChain由FilterChainProxy用来确定应该为这个请求调用哪个Spring Security过滤器

加上spring security 拦截器报错forbidden403_spring boot_05