Spring Security流程解释与配置
1.Spring Security流程解释
流程图
SpringSecurity 采用的是责任链的设计模式,它有一条很长的过滤器链
流程大致解释
客户端发起一个请求进入security的过滤链,将 Security 上下文异步映射继承之后 储存在SecurityContextPersistenceFilte中
走到登录判断之后,登出则是logoutHandler成功的话会到logoutSuccessHandler失败ExceptionTranslationFilter,如果不是登出的话则会进入下个过滤器
首先DefaultLoginPageGeneratingFilter处会查询有么有配置的登录页面如果有则跳入配置的没有则跳入默认的
然后判断是否为登录请求,如果是则进入UsernamePasswordAuthenticationFilter,如果登录失败则到 AuthenticationFailureHandler 登录失败处理器处理,如果登录成功则到AuthenticationSuccessHandler 登录成功处理器处理,如果不是登录请求则不进入该过滤器
具体参数解释
- WebAsyncManagerIntegrationFilter:将 Security 上下文与 Spring Web 中用于处理异步请求映射的 WebAsyncManager 进行集成。
- SecurityContextPersistenceFilter:在每次请求处理之前将该请求相关的安全上下文信息加载到 SecurityContextHolder 中,然后在该次请求处理完成之后,将 SecurityContextHolder 中关于这次请求的信息存储到一个“仓储”中,然后将 SecurityContextHolder 中的信息清除,例如在Session中维护一个用户的安全信息就是这个过滤器处理的。
- HeaderWriterFilter:用于将头信息加入响应中。
- CsrfFilter:用于处理跨站请求伪造。
- LogoutFilter:用于处理退出登录。
- DefaultLoginPageGeneratingFilter:如果没有配置登录页面,那系统初始化时就会配置这个过滤器,并且用于在需要进行登录时生成一个登录表单页面
- UsernamePasswordAuthenticationFilter:用于处理基于表单的登录请求,从表单中获取用户名和密码。默认情况下处理来自 /login 的请求。从表单中获取用户名和密码时,默认使用的表单 name 值为 username 和 password,这两个值可以通过设置这个过滤器的usernameParameter 和 passwordParameter 两个参数的值进行修改。 (后续代码中需要实现userDetailsService和passwordEncoder)
- BasicAuthenticationFilter:检测和处理 http basic 认证
- RequestCacheAwareFilter:用来处理请求的缓存
- SecurityContextHolderAwareRequestFilter:主要是包装请求对象request
- AnonymousAuthenticationFilter:检测 SecurityContextHolder 中是否存在 Authentication 对象,如果不存在为其提供一个匿名 Authentication
- SessionManagementFilter:管理 session 的过滤器
- ExceptionTranslationFilter:处理 AccessDeniedException 和 AuthenticationException 异常
- FilterSecurityInterceptor:可以看做过滤器链的出口
- RememberMeAuthenticationFilter:当用户没有登录而直接访问资源时, 从 cookie 里找出用户的信息, 如果 Spring Security 能够识别出用户提供的remember me cookie, 用户将不必填写用户名和密码, 而是直接登录进入系统,该过滤器默认不开启。
2.java配置
(1)导入xml
一般导入第一个就行了,第二个是将原本放入内存的session放入redis中 (可配可不配)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency>
(2)实现配置
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth){} @Override public void configure(WebSecurity web){} @Override protected void configure(HttpSecurity http) throws Exception {} }
(3)解释
1. configure(WebSecurity web)这个配置方法用于配置静态资源的处理方式,可使用 Ant 匹配规则。负责http web安全
configure(AuthenticationManagerBuilder auth)身份验证管理生成器,AuthenticationManager 的建造器,配置 AuthenticationManagerBuilder 会让Security 自动构建一个 AuthenticationManager(该类的功能参考流程图);如果想要使用该功能你需要配置一个 UserDetailService 和 PasswordEncoder。UserDetailsService 用于在认证器中根据用户传过来的用户名查找一个用户, PasswordEncoder 用于密码的加密与比对,我们存储用户密码的时候用PasswordEncoder.encode() 加密存储,在认证器里会调用 PasswordEncoder.matches() 方法进行密码比对。如果重写了该方法,Security 会启用 DaoAuthenticationProvider 这个认证器,该认证就是先调用 UserDetailsService.loadUserByUsername 然后使用 PasswordEncoder.matches() 进行密码比对,如果认证成功成功则返回一个 Authentication 对象。
3. configure(HttpSecurity http)HTTP请求安全处理,这个是最重要的
http.formLogin().loginPage("/login") .usernameParameter("username") .passwordParameter("password") .loginProcessingUrl("/sing_in").permitAll();
这个表示配置了以表单方式提交,登录页为login,登录用户名和密码,还有登录请求路径,并且支持全部用户访问
http .authorizeRequests() .antMatchers("/test").hasRole("test") .anyRequest().authenticated() .accessDecisionManager(accessDecisionManager());
权限相关的配置,配置了一个 /test的url 该有什么权限才能访问, anyRequest() 表示所有请求,authenticated() 表示已登录用户才能访问, accessDecisionManager() 表示绑定在 url 上的鉴权管理器
所以权限的配置自由度很高,鉴权器可以绑定在任意url上,还可以硬编码任意url
登出和登出成功处理器
http .logout() .logoutUrl("/logout") .logoutSuccessHandler(new MyLogoutSuccessHandler())
鉴权失败的处理器
http .exceptionHandling() .accessDeniedHandler(new MyAccessDeniedHandler());
插入自定义过滤器。addFilterBefore 加在对应的过滤器之前,addFilterAfter 加在对应的过滤器之后,addFilterAt 加在过滤器同一位置
框架原有的 Filter 在启动 HttpSecurity 配置的过程中,都由框架完成了其一定程度上固定的配置,是不允许更改替换的。根据测试结果来看,调用 addFilterAt 方法插入的 Filter ,会在这个位置上的原有 Filter 之前执行。
http.addFilterAfter(new MyFittler(), LogoutFilter.class); http.addFilterAt(getAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);