org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
org.springframework.security.web.context.SecurityContextPersistenceFilter
org.springframework.security.web.header.HeaderWriterFilter
org.springframework.security.web.csrf.CsrfFilter
org.springframework.security.web.authentication.logout.LogoutFilter
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
org.springframework.security.web.authentication.AnonymousAuthenticationFilter
org.springframework.security.web.session.SessionManagementFilter
org.springframework.security.web.access.ExceptionTranslationFilter
org.springframework.security.web.access.intercept.FilterSecurityInterceptor
主要过滤器 ExceptionTranslationFilter:异常处理过滤器,凡是在过滤器环节出现的错误都或转到该过滤器进行统一处理 UsernamePasswordAuthenticationFilter:默认的认证过滤器,从请求中读取表单用户名密码数据进行校验,如果是JSON提交需要对其进行重写 FilterSecurityInterceptor:是一个方法级的权限过滤器, 基本位于过滤链的最底部 自定义添加过滤器 除了SpringSecurity默认的过滤器,我们还可以添加自己的过滤器来进行自定义认证或授权。在SpringBoot开发中,SpringSecurity自动配置会向容器中自动注入相关过滤器,因此如果自己也有过滤器的情况下回导致过滤器顺序混乱,建议通过SpringSecurity统一管理过滤器。 如下,我们在自己的配置类中重写configure(HttpSecurity http)配置方法,自定义添加自己的过滤器
1 @Configuration //告诉SpringBoot该类是一个配置类,自动装配到IOC容器 2 @EnableWebSecurity //全局开启SpringSecurity 3 @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) //开启权限注解,之后会介绍 4 public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 5 @Override 6 protected void configure(HttpSecurity http) throws Exception { 7 //添加自定义过滤器在某过滤器前执行 8 http.addFilterBefore(wrapperFilter, UsernamePasswordAuthenticationFilter.class); 9 //添加自定义过滤器在某过滤器之后 10 http.addFilterAfter(filter,UsernamePasswordAuthenticationFilter.class); 11 //添加过滤器在最后 12 http.addFilter(filter); 13 } 14 }
配置常用组件: WebSecurityConfigurerAdapter(Security配置适配器) UsernamePasswordAuthentcationFilter(用户名密码认证过滤器) UserDetailService(用户权限数据查询服务) TokenRepository(记住我Token Dao) PasswordEncoder(NoOpPasswordEncoder、BCryptPasswordEncoder加密方式) HttpSecutity常用配置: Remember(记住我):
1 .rememberMe() 2 .tokenRepository(tokenRepository) //记住我的token管理Dao,操作数据库的记住我缓存 3 .tokenValiditySeconds(60) //token的有效时长formLogin(表单登录):
1 .formLogin() 2 .loginPage("/login_page") //登陆页面 3 .loginProcessingUrl("/login") //登陆请求处理接口,Spring security默认的处理登录接口是/login这个自带的接口 4 .usernameParameter("name") //指定用户名参数名称 5 .passwordParameter("passwd") //指定密码参数名称 6 .permitAll() //将登录操作url放行authorizeRequests(url请求权限):
.authorzeRequests() .antMatchers("/admin/**") .hasRole("ADMIN") //具备特定角色可访问 .antMatchers("/user/**") .access("hasAnyRole('ADMIN','USER')") //参数以表达式方式书写,多个以 and 连接 //除了.permitAll()的url,其余所有URL需要认证后访问 .anyRequest() .authenticated()
logout(登出):
1 .logout() //开启注销登陆 2 .logoutUrl("/logout") //注销登陆请求url 3 .clearAuthentication(true) //清除身份信息 4 .invalidateHttpSession(true) //session失效 5 .addLogoutHandler(new LogoutHandler() { //注销处理 6 @Override 7 public void logout(HttpServletRequest req, 8 HttpServletResponse resp, 9 Authentication auth) { 10 11 } 12 }) 13 .logoutSuccessHandler(new LogoutSuccessHandler() { //注销成功处理 14 @Override 15 public void onLogoutSuccess(HttpServletRequest req, 16 HttpServletResponse resp, 17 Authentication auth) 18 throws IOException { 19 resp.sendRedirect("/login_page"); //跳转到自定义登陆页面 20 } 21 })
csrf(跨站脚本伪造):
1 .csrf().disabl(); //前后端分离时一般关闭
RemenberMe记住我(自动登录)原理: 总结配置流程- 认证用户实现UserDetails接口
- 用户来源的Service实现UserDetailsService接口,实现loadUserByUsername()方法,从数据库中获取数据
- 实现自己的过滤器继承UsernamePasswordAuthenticationFilter,重写attemptAuthentication()和successfulAuthentication()方法实现自己的逻辑
- Spring Security的配置类继承自WebSecurityConfigurerAdapter,重写里面的两个config()方法
- 如果使用RSA非对称加密,就准备好RSA的配置类,然后在启动类中加入注解将其加入IOC容器
- securedEnable=true 开启@Securd注解
- prePostEnable=true 开启@Preauthorize@PostAuthorize注解
1 @Service 2 public class MethodService { 3 @Secured("ROLE_ADMIN") //访问此方法需要ADMIN角色 4 public String admin() { 5 return "hello admin"; 6 } 7 @PreAuthorize("hasRole('ADMIN') and hasRole('DBA')") //访问此方法需要ADMIN且DBA 8 public String dba() { 9 return "hello dba"; 10 } 11 @PreAuthorize("hasAnyRole('ADMIN','DBA','USER')") //三个都行 12 public String user() { 13 return "user"; 14 } 15 }
参数过滤 @PostFilter:对返回值进行过滤 @PreFilter:对方法参数进行过滤 用法: @PostFilter("filterObject.属性==xx"):过滤对象属性,当表达式为true时允许数据通过,为false的数据会被过滤掉 开起security 注解 @EnableWebSecurity在 Spring boot 应用中使用 Spring Security,用到了 @EnableWebSecurity注解,官方说明为,该注解和 @Configuration 注解一起使用, 注解 WebSecurityConfigurer 类型的类,或者利用@EnableWebSecurity 注解继承 WebSecurityConfigurerAdapter的类,这样就构成了 Spring Security 的配置。 单点登录相关
SpringSecurity总结理解: SpringSecurity的配置非常灵活可拓展性很强,因此使用该安全组件可以完成很多自定义配置。首先分析项目中的几个要素:认证路径(登录处理)、需要放行的路径,需要授权的路径、是否使用session、是否禁用csrf、配置跨域过滤器、是否自定义认证成功后失败的逻辑处理、用户认证失败或权限不足的响应逻辑。 就以我现在的项目分析: 对于前后端分离的项目首先要考虑到禁用session和csrf因为我们用jwt token来解决http的无状态性,需要注意的是禁用session后SecurityContextHolder会失效,因为它默认依赖session来进行上下文处理。 然后对于静态资源的url放行处理,和对公开访问接口的放行处理,例如登录处理接口、注册接口、验证码获取接口等。做完这些放行处理之后,再对剩下所有的url进行认证拦截处理。 再然后就需要做对于token认证授权的一些适配配置问题进行自定义处理,比如登录成功后我们需要将用户的token存入redis,那么这个逻辑可以通过实现UsernamePasswordAuthenticationFilter然后重写successfulAuthentication和unsuccessfulAuthentication方法,在successfulAuthentication方法中我们可以自定义登录成功后的处理逻辑,如token处理和security上下文处理等。unsuccessfulAuthentication一般就返回失败就行了。注意我们自己实现的UsernamePasswordAuthenticationFilter想让他生效需要在配置中让他替换原来的通过http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); 还有一种方法通过http.formLogin().successHandler().failureHandler();来设置认证成功和失败的处理器; 注意,如果我们使用token来维护状态,那么每个请求都是需要通过token来鉴权的,所以我们还需要写一个过滤器来维护token校验逻辑,写一个过滤器获取请求中的token做校验逻辑,校验处理成功后我们还可以将用户数据添加到SecurityContextHolder中,以便在后续处理中获取当前请求用户数据信息。 还有一个需要注意的地方,关于用户的认证校验逻辑,一般我们通过实现UsernamePasswordAuthenticationFilter并重写attemptAuthentication方法进行认证校验。还有一种方法是不实现UsernamePasswordAuthenticationFilter,而是提供一个我们的身份提供者,一个实现了AuthenticationProvider接口的类,然后将认证校验逻辑卸载authenticate()方法中,并且通过authenticationProvider()方式进行配置。 由过滤器链组成,通过配置可以开启或关闭特定过滤器,其中认证过滤器最终都会创建一个对应的XXXToken,例如UsernamePasswordAuthentication会创建一个UsernamePasswordAuthenticationToken。创建好了之后通过实现了authenticationManager接口的ProviderManager遍历所有实现了AuthenticationProvider身份提供者进行身份校验逻辑。注意每个ProviderManager都只处理自己关联的Token,如RememberMeAuthenticationProvider只处理RememberMeAuthenticationToken。当遍历到其中一个ProviderManager能够成功验证时用户就认证成功了。