五SpringSecurity重要配置类--1SecurityFilterChain
五 SpringSecurity重要配置类--1SecurityFilterChain
SecurityFilterChain作用其实跟Servlet的FilterChain一样,同样维护了很多Filters,这些Filters 是由Spring Security提供的,每个 Security Filter 都有不同的职能,比如登录认证、CSRF防御...如下图所示:
同时,Spring Security 支持添加多个SecurityFilterChain,每个SecurityFilterChain负责不同的请求(比如依据请求地址进行区分),这样可以为不同的请求设置不同的认证规则。其源码如下所示:
public interface SecurityFilterChain {
// 匹配请求
boolean matches(HttpServletRequest request);
// 获取该 SecurityFilterChain 中的所有 Filter
List<Filter> getFilters();
}
具体来说,当请求到达FilterChainProxy时,其内部会根据当前请求匹配得到对应的SecurityFilterChain,然后将请求依次转发给到该SecurityFilterChain中的所有 Security Filters。如下图所示:
4.3.0 DefaultSecurityFilterChain(过滤器链唯一实现)
SecurityFilterChain 接口只有一个实现类,那就是 DefaultSecurityFilterChain:
public final class DefaultSecurityFilterChain implements SecurityFilterChain {
private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
private final RequestMatcher requestMatcher;
private final List<Filter> filters; //当前chain上对应的filters
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
this(requestMatcher, Arrays.asList(filters));
}
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
logger.info("Creating filter chain: " + requestMatcher + ", " + filters);
this.requestMatcher = requestMatcher;
this.filters = new ArrayList<>(filters);
}
public RequestMatcher getRequestMatcher() {
return requestMatcher;
}
public List<Filter> getFilters() {
return filters;
}
public boolean matches(HttpServletRequest request) {
return requestMatcher.matches(request);
}
@Override
public String toString() {
return "[ " + requestMatcher + ", " + filters + "]";
}
}
DefaultSecurityFilterChain 只是对 SecurityFilterChain 中的方法进行了实现,并没有特别值得说的地方,松哥也就不啰嗦了。
那么从上面的介绍中,DefaultSecurityFilterChain 其实就相当于是 Spring Security 中的过滤器链,一个 DefaultSecurityFilterChain 代表一个过滤器链,如果系统中存在多个过滤器链,则会存在多个 DefaultSecurityFilterChain 对象
4.3.1 多个过滤器链的配置
在前面讲述和实例中,过滤器链配置如下:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated()
...
.csrf().disable();
}
这是一个过滤器链。因为不管是 /admin/**
还是 /user/**
,走过的过滤器都是一样的,只是不同的路径判断条件不一样而已。
如果系统需要配置多个过滤器链,需要在 FilterChainProxy按照下图进行划分的情况:
可以看到,当请求到达 FilterChainProxy 之后,FilterChainProxy 会根据请求的路径,将请求转发到不同的 Spring Security Filters 上面去,不同的 Spring Security Filters 对应了不同的过滤器,也就是不同的请求将经过不同的过滤器。
多个过滤器链的配置如下:
@Configuration
public class SecurityConfig {
@Bean
protected UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("javaboy").password("{bcrypt}$2a$10$Sb1gAUH4wwazfNiqflKZve4Ubh.spJcxgHG8Cp29DeGya5zsHENqi").roles("admin", "aaa", "bbb").build());
manager.createUser(User.withUsername("sang").password("{noop}123").roles("admin").build());
manager.createUser(User.withUsername("江南一点雨").password("{MD5}{Wucj/L8wMTMzFi3oBKWsETNeXbMFaHZW9vCK9mahMHc=}4d43db282b36d7f0421498fdc693f2a2").roles("user", "aaa", "bbb").build());
return manager;
}
@Configuration
@Order(1)
static class DefaultWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/foo/**")
.authorizeRequests()
.anyRequest().hasRole("admin")
.and()
.csrf().disable();
}
}
@Configuration
@Order(2)
static class DefaultWebSecurityConfig2 extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/bar/**")
.authorizeRequests()
.anyRequest().hasRole("user")
.and()
.formLogin()
.permitAll()
.and()
.csrf().disable();
}
}
}
1. 首先,SecurityConfig 不再需要继承自 WebSecurityConfigurerAdapter 了,只是作为一个普通的配置类,加上 @Configuration 注解即可。
2. 提供 UserDetailsService 实例,相当于是我们的数据源。
3. 创建静态内部类继承 WebSecurityConfigurerAdapter 类,同时用 @Configuration 注解标记静态内部类是一个配置类,配置类里边的代码就和之前的一样了,无需赘述。
4. 每一个静态内部类相当于就是一个过滤器链的配置。
5. 注意在静态内部类里边,我没有使用 `http.authorizeRequests()` 开始,`http.authorizeRequests()` 配置表示该过滤器链过滤的路径是 `/**`。在静态内部类里边,我是用了 `http.antMatcher("/bar/**")` 开启配置,表示将当前过滤器链的拦截范围限定在 `/bar/**`。
6. 当存在多个过滤器链的时候,必然会有一个优先级的问题,所以每一个过滤器链的配置类上通过 @Order(2) 注解来标记优先级。
从上面这段代码中大家可以看到,configure(HttpSecurity http) 方法就是在配置过滤器链。我们在该方法中的配置,都是在添加/移除/修改 Spring Security 默认提供的过滤器,所以该方法就是在配置 Spring Security 中的过滤器链,至于是怎么配置的,详见随后章节。
总结:
首先,http.authorizeRequests()
配置并非总在第一行出现,如果只有一个过滤器链,他总是在第一行出现,表示该过滤器链的拦截规则是 /**
(请求只有先被过滤器链拦截下来,接下来才会进入到不同的 Security Filters 中进行处理),如果存在多个过滤器链,就不一定了。
仅仅从字面意思来理解,authorizeRequests() 方法的返回值是 ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry,ExpressionUrlAuthorizationConfigurer 可以为多组不同的 RequestMatcher 配置不同的权限规则,就是大家看到的 .antMatchers("/admin/**").hasRole("admin").antMatchers("/user/**").hasRole("user")
。
4.3.2 spring security执行机制
Spring Security 作用机制大致如下:
- 注册标准Filter:首先,Spring 会自动注入一个DelegatingFilterProxy到 Servlet 的FilterChain中。
- 请求转发到 Spring Security:当请求到来时,DelegatingFilterProxy就会自动在 Spring容器 中搜索名称为springSecurityFilterChain的Filter实体,其实际类型为FilterChainProxy。DelegatingFilterProxy最终会将请求转发给到FilterChainProxy。
- 找到匹配请求处理的SecurityFilterChain:FilterChainProxy内部维护了一系列SecurityFilterChains,他会依据请求内容找到对应处理该请求的SecurityFilterChain。
- 请求处理:找到能处理请求的第一个SecurityFilterChain后,就会遍历该SecurityFilterChain内部维护的一系列Filters,依次让这些 Security Filter 处理该请求,完成认证、授权等功能。