一、过滤器链
security底层就是由多个过滤器组成的过滤器链:
WebAsyncManagerIntegrationFilter:将 Security 上下文与 Spring Web 中用于处理异步请求映射的 WebAsyncManager 进行集成。
SecurityContextPersistenceFilter:在每次请求处理之前将该请求相关的安全上下文信息加载到 SecurityContextHolder 中,然后在该次请求处s理完成之后,将 SecurityContextHolder 中关于这次请求的信息存储到本地,然后将 SecurityContextHolder 中的信息清除,例如在session中维护一个用户的安全信息就是这个过滤器处理的。
HeaderWriterFilter:用于将头信息加入响应中。
CsrfFilter:用于处理跨站请求伪造。
LogoutFilter:用于处理退出登录。
UsernamePasswordAuthenticationFilter:用于处理基于表单的登录请求,从表单中获取用户名和密码。默认情况下处理来自 /login 的请求。从表单中获取用户名和密码时,默认使用的表单 name 值为 username 和 password,这两个值可以通过设置这个过滤器的usernameParameter 和 passwordParameter 两个参数的值进行修改。
DefaultLoginPageGeneratingFilter:如果没有配置登录页面,那系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面。
BasicAuthenticationFilter:检测和处理 http basic 认证。
RequestCacheAwareFilter:用来处理请求的缓存。
SecurityContextHolderAwareRequestFilter:主要是包装请求对象request。
AnonymousAuthenticationFilter:检测 SecurityContextHolder 中是否存在 Authentication 对象,如果不存在为其提供一个匿名 Authentication。
SessionManagementFilter:管理 session 的过滤器
ExceptionTranslationFilter:处理 AccessDeniedException 和 AuthenticationException 异常。
FilterSecurityInterceptor:可以看做过滤器链的出口。
RememberMeAuthenticationFilter:当用户没有登录而直接访问资源时, 从 cookie 里找出用户的信息, 如果 Spring Security 能够识别出用户提供的remember me cookie, 用户将不必填写用户名和密码, 而是直接登录进入系统,该过滤器默认不开启。
二、放行资源的方式
第一种:放行静态资源
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/index.html", "/img/**", "/fonts/**", "/favicon.ico", "/verifyCode");
}
第二种:放行需要使用到用户信息的接口
http.authorizeRequests()
.antMatchers("/hello").permitAll()
.anyRequest().authenticated()
两种放行资源的方式是有区别的!!!第一种是直接放行,资源不会经过security的过滤器链,也就是该资源和security无关了;第二种也是放行,但会经过security的过滤器链,这里踩过一个坑,就是在需要获取到用户信息的时候,但又希望这个接口是不需要权限的,比如自己实现安全退出,需要去使acess_token和refresh_token失效,然后清空session,如果我们走第一种放行,那么就不会经过security上下文持久化(Persistence)的过滤器,SecurityContext里就没有用户信息。
这里也就又引出一个问题,那么security是怎么存储用户信息的?
我们获取用户信息是通过SecurityContextHolder,SecurityContextHolder的数据,实际是存在ThreadLocal中的,ThreadLocal的特点是,存储在它里面的数据,哪个线程存的,哪个线程才能拿。当用户登录后(thread1),SecurityContextHolder保存了用户的信息,然后该次请求完毕后,为了保证下一次请求(线程)能拿到用户的数据,此时security把SecurityContextHolder的数据放到session里面,这样下一次请求进来经过SecurityContextPersistence就可以把session中的用户信息加载到SecurityContextHolder中,这样本次请求需要用到用户信息的地方,就可以直接从SecurityContextHolder里拿了。
三、过滤器的工作流程
1.Security的过滤器链UsernamePasswordAuthenticationFilter拦截到用户请求后,将其封装到请求Authentication中,这里的实现类是UsernamePasswordAuthenticationToken,把用户输入的账号和密码传进去。
2.然后过滤器把封装好的Authentication交给AuthenticationManager,认证管理器调用authenticate方法,之后根据用户名查询数据库,这里我们需要实现UserDetailsService,实现loadByUsername方法,根据这个方法名字我们就知道,我们是通过用户名去数据库查询是否有这个用户,然后把查询出来的用户交给Security去验证matches密码是否正确,在loadByUsername方法中需要返回security的user,也就是实现userdetails接口的user,里面有四个boolean类型的参数,就是看用户有没有凭证或者是否被禁用,还有getAccount和getPassword和getAuthorities可以获取用户的信息。
3.认证成功后,AuthenticationManager身份管理器返回一个被填充信息的(权限、用户名等)Authentication实例。
4.SecurityContextHolder安全上下文容器把认证成功的authentication实例set到SecurityContext里面。可以看到AuthenticationManager接口是认证相关的核心接口,也是发起认证的出发点,他的实现类是ProvideManager。而SpringSecurity支持多种认证方式,因此ProviderManager维护着一个List集合,存放多种认证方式,最终实际的认证工作是由AuthenticationProvider完成的。web表单对应的Provider是DaoAuthenticationProvider,里面又维护了一个UserDetailsService负责UserDetails的获取,最终由AuthenticationProvider把UserDetails填充到Authentication。
SpringBoot整合Spring Security OAuth2.0认证授权_Qblue666的博客-CSDN博客_springboot整合oauth2.0