Spring Security(4)——springSecurity框架如何使用多个过滤器链(附完整项目代码)
1. 先看看多个过滤器链结果
这部分内容比较多,建议读者下载项目启动后再看。直接查看配置方式的话,请看第二部分。
我的demo项目启动时候,打印的日志如下所示,显示系统生成了3个FilterChain。
第一个FilterChain
2021-02-08 14:14:07.614 INFO 18200 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/user/**'], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@4f82663e, org.springframework.security.web.context.SecurityContextPersistenceFilter@ec1b2e4, org.springframework.security.web.header.HeaderWriterFilter@34a0ef00, org.springframework.security.web.authentication.logout.LogoutFilter@58a120b0, com.springsecurity.filter.UserFilter@e04ccf8, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@971e903, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@67e28be3, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@597f48df, org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter@3e4f80cb, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@51d143a1, org.springframework.security.web.session.SessionManagementFilter@21fdfefc, org.springframework.security.web.access.ExceptionTranslationFilter@6293e39e, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@50f40653]
第二个FilterChain
2021-02-08 14:14:07.617 INFO 18200 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: Ant [pattern='/admin/**'], [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7cb2651f, org.springframework.security.web.context.SecurityContextPersistenceFilter@66bacdbc, org.springframework.security.web.header.HeaderWriterFilter@77c7ed8e, org.springframework.security.web.authentication.logout.LogoutFilter@4b54af3d, com.springsecurity.filter.AdminFilter@4441d567, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@b8e246c, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2c6ee758, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@640dc4c6, org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter@1f387978, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@3e1624c7, org.springframework.security.web.session.SessionManagementFilter@453d496b, org.springframework.security.web.access.ExceptionTranslationFilter@191a709b, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@616b241a]
第三个FilterChain
2021-02-08 14:14:07.621 INFO 18200 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7144655b, org.springframework.security.web.context.SecurityContextPersistenceFilter@64c4c01, org.springframework.security.web.header.HeaderWriterFilter@79d743e6, org.springframework.security.web.authentication.logout.LogoutFilter@6ee8dcd3, com.springsecurity.filter.CommonFilter@5c82cd4f, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@3c6aa04a, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@1aa99005, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@592238c5, org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter@2257fadf, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@35835e65, org.springframework.security.web.session.SessionManagementFilter@776802b0, org.springframework.security.web.access.ExceptionTranslationFilter@2b56f5f8, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@bc4d5e1]
3个过滤器链的初始化顺序是Ant [pattern='/user/'],Ant [pattern='/admin/'],any request。
这个跟我springSecurity配置中3个配置类的@Order注解配置的编号大小顺序是一致的,CommonWebSecurityConfig>UserWebSecurityConfig>AdminWebSecurityConfig。
3个FilterChain的filter顺序如日志所示,UsernamePasswordAuthenticationFilter过滤器在UserFilter,AdminFilter,CommonFilter之后执行,因此过滤器链路的责任链执行顺序就是跟打印的顺序一样。
启动demo项目后,登入系统系列操作,查看打印的日志。
- 进入http://localhost:8082/,打印日志如下
因为CommonWebSecurityConfig排序靠后的,匹配/user/,/admin/的url都进入到前两个过滤器链执行了。其他没有匹配规则的url都进入了CommonWebSecurityConfig的过滤器链。这点从打印的日志内容就能看出。CommonFilter 在 user 过滤链的 UsernamePasswordAuthenticationFilter 前调用 , 过滤器拦截的url是:/ CommonFilter 在 user 过滤链的 UsernamePasswordAuthenticationFilter 前调用 , 过滤器拦截的url是:/css/main.css CommonFilter 在 user 过滤链的 UsernamePasswordAuthenticationFilter 前调用 , 过滤器拦截的url是:/error
url“/“、“/css/main.css“、“/error“都不满足user以及admin过滤器链的匹配规则,都进入到了Common链。
- 在home页面访问admin page
进入后,跳转到登入页面,因为此时还未登入。日志打印如下
AdminFilter在 admin 过滤链的 UsernamePasswordAuthenticationFilter 前调用 , 过滤器拦截的url是:/admin
CommonFilter 在 user 过滤链的 UsernamePasswordAuthenticationFilter 前调用 , 过滤器拦截的url是:/login
CommonFilter 在 user 过滤链的 UsernamePasswordAuthenticationFilter 前调用 , 过滤器拦截的url是:/css/main.css
CommonFilter 在 user 过滤链的 UsernamePasswordAuthenticationFilter 前调用 , 过滤器拦截的url是:/error
CommonFilter 在 user 过滤链的 UsernamePasswordAuthenticationFilter 前调用 , 过滤器拦截的url是:/login
输入用户名密码 root/666666,登录后自动跳转到/admin路径。如下所示跳转后的页面以及打印的日志。
日志:
AdminFilter在 admin 过滤链的 UsernamePasswordAuthenticationFilter 前调用 , 过滤器拦截的url是:/admin
CommonFilter 在 user 过滤链的 UsernamePasswordAuthenticationFilter 前调用 , 过滤器拦截的url是:/error
CommonFilter 在 user 过滤链的 UsernamePasswordAuthenticationFilter 前调用 , 过滤器拦截的url是:/css/main.css
CommonFilter 在 user 过滤链的 UsernamePasswordAuthenticationFilter 前调用 , 过滤器拦截的url是:/error
从日志可以看出,浏览器发出/admin请求,校验后权限不够,就进入error页面,提示用户权限不够。
/admin的权限配置如下,是ADMIN001,root用户的权限只有ADMIN、USER,因此无法访问。将ADMIN001换成ADMIN,重启应用就能正常访问了。
.antMatchers("/admin/**").hasAnyRole("ADMIN001") // 满足该条件下的路由需要ROLE_ADMIN的角色
以上,就是我的demo项目的测试过程,大家可以试着操作下。
2. springSecurity添加多个过滤器链的方法
这部分说的多个过滤器链配置方式是要求读者有Security配置经验基础上介绍的,如果不熟悉security基本配置,请查看网站的SpringSecurity系列文章Java架构师方案——Spring Security(一)快速入门(附完整项目代码)
security配置类去掉注解@EnableWebSecurity,只用@Configuration注解配置即可,作为一个普通的configuration配置类。
配置一个configureGlobal方法,使用@Autowired修饰。在方法中使用AuthenticationManagerBuilder对象注入UserDetailsService对象。配置用户名密码及权限。
demo配置了3个内部静态配置类,3个配置类的优先级关系是:UserWebSecurityConfig>AdminWebSecurityConfig>CommonWebSecurityConfig。是由@order注解决定的。url匹配顺序也是按照这个优先级来的。
3个静态配置类就是springSecurity的3个过滤器链,三个过滤器链,匹配/admin/的url进入AdminWebSecurityConfig过滤链,匹配/user/的url进入UserWebSecurityConfig过滤链,其他的url就进入CommonWebSecurityConfig过滤链。
注意,普通的SpringSecurity在configure(HttpSecurity http)方法中配置方式是以http.authorizeRequests()开始的,而在配置多个过滤链的场景中,就不是之前的配置方式了,而是以http.antMatcher("/user/**")开始,后续再加上.authorizeRequests()方法。其配置就一样了。
6. 还要注意的是@order注解配置的优先级,不能让CommonWebSecurityConfig过滤器配置类优先级高,不然所有的/admin/以及/user/访问路径都会只走CommonWebSecurityConfig过滤器链。
*3. 多个过滤器链配置类实现代码
@Configuration
public class SecurityConfiguration {
@Autowired
private AccessDeniedHandler accessDeniedHandler;
@Autowired
private DataSource dataSource;
/*
* 不使用 auth.inMemoryAuthentication().withUser的api,只是用InMemoryUserDetailsManager方法。
*
* InMemoryUserDetailsManager 类的方法 能让方法都能正常 登录,没有任何错误。
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());//配置userdetail必不可少
// auth.inMemoryAuthentication().withUser(User.withUsername("jack").password("{bcrypt}" + new BCryptPasswordEncoder().encode("88888888")).roles("USER"));
// auth.inMemoryAuthentication().withUser(User.withUsername("king").password(passwordEncoder().encode("88888888")).roles("ADMIN","USER"));
// auth.inMemoryAuthentication().withUser(User.withUsername("root").password(passwordEncoder().encode("88888888")).roles("ADMIN","USER"));
}
public UserDetailsService userDetailsService() {
// TODO Auto-generated method stub
//直接建两个用户存在内存中,生产环境可以从数据库中读取,对应管理器JdbcUserDetailsManager
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
// 创建两个用户
//通过密码的前缀区分编码方式,推荐,这种加密方式很好的利用了委托者模式,使得程序可以使用多种加密方式,并且会自动
//根据前缀找到对应的密码编译器处理。
manager.createUser(User.withUsername("guest").password("{bcrypt}" +
new BCryptPasswordEncoder().encode("111111")).roles("USER").build());
manager.createUser(User.withUsername("root").password("{sha256}" +
new StandardPasswordEncoder().encode("666666"))
.roles("ADMIN", "USER").build());
System.out.println("获取用户信息:userDetailsService");
return manager;
}
/**
* 支持多种编码,通过密码的前缀区分编码方式,推荐
*
* @return the password encoder
*/
@Bean
PasswordEncoder passwordEncoder() {
PasswordEncoder PasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
PasswordEncoder.upgradeEncoding("{sha256}");
return PasswordEncoder;
}
@Configuration
@Order(3)
static class AdminWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/admin/**")
.authorizeRequests()
.antMatchers("/admin/**").hasAnyRole("ADMIN001") // 满足该条件下的路由需要ROLE_ADMIN的角色
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/admin/admin")//默认的成功跳转到的url
.failureUrl("/403")
.and()
.rememberMe()
.and()
.logout()
.permitAll()
.and()
.csrf().disable();
http.addFilterBefore(new AdminFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
@Configuration
@Order(2)
static class UserWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:on
http.antMatcher("/user/**")
.authorizeRequests()
// .antMatchers("/css/**", "/js/**", "/fonts/**").permitAll() // 允许访问资源
// .antMatchers("/", "/home", "/about", "/login").permitAll() //允许访问这三个路由
.antMatchers("/user/**").hasAnyRole("USER") // 满足该条件下的路由需要ROLE_USER的角色
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
// .loginProcessingUrl("/login")
// .usernameParameter("username")
// .passwordParameter("password")
// .successForwardUrl("/admin")
.defaultSuccessUrl("/user/user")//默认的成功跳转到的url
.failureUrl("/403")
.and()
.rememberMe()
.and()
.logout()
.permitAll()
.and()
.csrf().disable();
http.addFilterBefore(new UserFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
@Configuration
@Order(4)
static class CommonWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:on
http.authorizeRequests()
.antMatchers("/css/**", "/js/**", "/fonts/**").permitAll() // 允许访问资源
.antMatchers("/", "/home", "/about", "/login").permitAll() //允许访问这三个路由
.antMatchers("/user/**").hasAnyRole("USER") // 满足该条件下的路由需要ROLE_USER的角色
.antMatchers("/admin/**").hasAnyRole("ADMIN") // 满足该条件下的路由需要ROLE_ADMIN的角色
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
// .loginProcessingUrl("/login")
// .usernameParameter("username")
// .passwordParameter("password")
// .successForwardUrl("/admin")
.defaultSuccessUrl("/admin/admin")//默认的成功跳转到的url
.failureUrl("/403")
.and()
.rememberMe()
.and()
.logout()
.permitAll()
.and()
.csrf().disable();
http.addFilterBefore(new CommonFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
}
完整的demo项目,请关注公众号“前沿科技bot“并发送"SEC-FIVE"获取。
- 本文标签: Spring Boot Spring Security
- 版权声明: 本站原创文章,于2021年02月08日由空白发布,转载请注明出处