Lingmoumou's Blog

きっといつかって愿うまま

0%

Spring Security 基本原理

在web应用开发中,安全无疑是十分重要的,选择Spring Security来保护web应用是一个非常好的选择。Spring Security 是spring项目之中的一个安全模块,可以非常方便与spring项目无缝集成。

简介

Spring Security 的核心主要是一系列的过滤器链。

Spring Security 在 Servlet 的过滤链(filter chain)中注册了一个过滤器 FilterChainProxy,它会把请求代理到 Spring Security 自己维护的多个过滤链,每个过滤链会匹配一些 URL,如果匹配则执行对应的过滤器。过滤链是有顺序的,一个请求只会执行第一条匹配的过滤链。Spring Security 的配置本质上就是新增、删除、修改过滤器。

Spring Security的过滤链
Spring Security的过滤链

本质上是一连串的Filter, 然后又以一个独立的Filter的形式插入到Filter Chain里,其名为FilterChainProxy。

Spring Security的过滤链

实际上FilterChainProxy下面可以有多条Filter Chain,来针对不同的URL做验证,而Filter Chain中所拥有的Filter则会根据定义的服务自动增减。所以无需要显示再定义这些Filter,除非想要实现自己的逻辑。

FilterChainProxy

  • 绿色部分
    代表过滤器,每个方块都代表一个过滤器。

    • UsernamePasswordAuthenticationFilter用来处理表单登录的。检查当前的请求是不是一个登录请求,然后再这个请求里面带没带用户名和密码,如果带了用户名和密码,这个过滤器就会用用户名和密码尝试去登录,如果这个请求里面没有带用户名和密码,继续进行下面的过滤器。
    • BasicAuthenticationFilter用来处理HTTPBasic登录的。检查请求的请求头中是不是有Basic开头的authentication的信息,如果有的话,会尝试拿出来做basic流的字节码然后从中取出用户名和密码尝试做登录。
      springsecurity还提供了许多其他的认证方式,任何一个过滤器,成功的完成用户登录以后,都会再这个请求上做个标记,表明这个用户已经认证成功了。
  • 蓝色部分
    在Exception Translation Filter中,捕获橙色部分FilterSecurity Interceptor抛出的异常,会根据抛出的异常,会做相应的处理。

  • 橙色部分
    请求经历过这些过滤器后,就会到达这个写橘黄色的拦截器上比如FilterSecurity Interceptor,这个拦截器是整个Spring Security过滤器的最后一环,是最终的守门人,在它身后是我们自己写的controller的rest服务,在该拦截器中,它会去决定当前的请求能不能去访问后面真正的服务。如果验证不通过,则会往前抛出异常。

绿色的部分可以通过配置来设置是否生效,除了绿色之外的过滤器,是不能通过配置来控制的,而且位置也不能更改,不能移除到过滤器链外的。

FilterSecurityInterceptor.java

在FilterSecurityInterceptor中,

  1. 我们可以看到所有的判断逻辑是在beforeInvocation中执行的。
  2. 当判断逻辑通过后,执行doFilter,是真正开始调用controller中的restful服务了。

ExceptionTranslationFilter.java

在ExceptionTranslationFilter中,主要是用来处理异常的,

  1. 其中doFilter方法比较简单,主要是调用后面过滤器中的doFilter方法。
  2. 主要逻辑主要是在catch中,捕获到异常以后如何处理。

UsernamePasswordAuthenticationFilter.java

在UsernamePasswordAuthenticationFilter中,主要完成了对登录的验证,

  1. 只处理POST类型的/login的请求
  2. 收到请求后,从Request中拿到用户名与密码,然后进行登录。

两种认证方式

HTTP Basic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// BrowserSecurityConfig.java

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
}

Form表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// BrowserSecurityConfig.java

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
}

过滤器链中常用的过滤器

FilterChainProxy.java

SpringSecurity 采用的是责任链的设计模式,它有一条很长的过滤器链。现在对这条过滤器链的各个进行说明:

  • WebAsyncManagerIntegrationFilter:将 Security 上下文与 Spring Web 中用于处理异步请求映射的 WebAsyncManager 进行集成。
  • SecurityContextPersistenceFilter:在每次请求处理之前将该请求相关的安全上下文信息加载到 SecurityContextHolder 中,然后在该次请求处理完成之后,将 SecurityContextHolder 中关于这次请求的信息存储到一个“仓储”中,然后将 SecurityContextHolder 中的信息清除,例如在Session中维护一个用户的安全信息就是这个过滤器处理的。
  • HeaderWriterFilter:用于将头信息加入响应中。
  • CsrfFilter:用于处理跨站请求伪造。
  • LogoutFilter:用于处理退出登录。
  • UsernamePasswordAuthenticationFilter:用于处理基于表单的登录请求,从表单中获取用户名和密码。默认情况下处理来自 /login 的请求。从表单中获取用户名和密码时,默认使用的表单 name 值为 username 和 password,这两个值可以通过设置这个过滤器的usernameParameter 和 passwordParameter 两个参数的值进行修改。
  • DefaultLoginPageGeneratingFilter:如果没有配置登录页面,那系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面。
  • DefaultLogoutPageGeneratingFilter:如果没有配置登出页面,那系统初始化时就会配置这个过滤器。
  • BasicAuthenticationFilter:检测和处理 http basic 认证。
  • RequestCacheAwareFilter:用来处理请求的缓存。
  • SecurityContextHolderAwareRequestFilter:主要是包装请求对象request。
  • RememberMeAuthenticationFilter:当用户没有登录而直接访问资源时, 从 cookie 里找出用户的信息, 如果 Spring Security 能够识别出用户提供的remember me cookie, 用户将不必填写用户名和密码, 而是直接登录进入系统,该过滤器默认不开启。
  • AnonymousAuthenticationFilter:检测 SecurityContextHolder 中是否存在 Authentication 对象,如果不存在为其提供一个匿名 Authentication。
  • SessionManagementFilter:管理 session 的过滤器
  • ExceptionTranslationFilter:处理 AccessDeniedException 和 AuthenticationException 异常。
  • FilterSecurityInterceptor:可以看做过滤器链的出口。

参考文献