我们先来看一个简单的配置:

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/doLogin")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.permitAll()
.and()
.csrf().disable();
}

这样的配置在 Spring Security 中很常见,通过 and 方法,可以将所有的配置连接在一起,一条线下来,所有的东西都配置好了。

但是有小伙伴对这里的 and 表示很迷,不知道什么时候 and 方法该出场,什么时候 and 不该出场!

所以今天松哥就花点时间来和大家聊一下这里的 and 方法,希望大家看完完整后,能够明白 and 到底怎么玩!

1.原始配置

在 Spring Boot 出现之前,我们使用 Spring Security ,都是通过 XML 文件来配置 Spring Security 的,即使现在大家在网上搜索 Spring Security 的文章,还是能够找到很多 XML 配置的。

但是小伙伴们明白,无论是 XML 配置还是 Java 配置,只是在用不同的方式描述同一件事情,从这里角度来看,我们现在所使用的 Java 配置,和以前使用的 XML 配置,应该有某种异曲同工之妙。

可能有小伙伴没见过 XML 配置的 Spring Security,我在这里给大家简单举几个例子:

<http>
<intercept-url pattern="/login" access="permitAll" />
<form-login login-page="/login" />
<http-basic />
</http>

这段 XML 大家稍微瞅一眼大概就能明白其中的含义:


  1. intercept-url 相当于配置拦截规则
  2. form-login 是配置表单登录
  3. http-basic 是配置 HttpBasic 认证

如果我们使用了 Java 配置,这些 XML 配置都有对应的写法,例如 ​​.anyRequest().authenticated()​​​ 就是配置拦截规则的,​​.formLogin()​​ 是配置表单登录细节的。

仅仅从语义层面来理解,and 有点类似于 XML 中的结束标签,每当 and 出现,当前的配置项就结束了,可以开启下一个配置了。

那么从代码层面上,这个要如何理解呢?

2.代码层面的理解

小伙伴们知道,Spring Security 中的功能是由一系列的过滤器来实现的,默认的过滤器一共有 15 个,这 15 个过滤器松哥以后会和大家挨个介绍。

每一个过滤器都有一个对应的 configurer 来对其进行配置,例如我们常见的 UsernamePasswordAuthenticationFilter 过滤器就是通过 AbstractAuthenticationFilterConfigurer 来进行配置的。

这些 configure 都有一个共同的父类,那就是 SecurityConfigurer,给大家大致看一下 SecurityConfigurer 的继承关系图:

Spring Security 配置中的 and 到底该怎么理解?_spring

可以看到,它的实现类还是蛮多的。

SecurityConfigurer 的源码很简单:

public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
void init(B builder) throws Exception;
void configure(B builder) throws Exception;
}

就两个方法,第一个 init 用来做初始化操作,第二个 configure 用来做具体的配置。

在 Spring Security 框架初始化的时候,会把所有的这些 xxxConfigurer 收集起来,然后再统一调用每一个 xxxConfigurer 里边的 init 和 configure 方法(松哥在以后的文章中会和大家详细讨论这个过程),调用完成后,Spring Security 默认的过滤器链就形成了。

这就是我们所说的 xxxConfigurer 的作用!

文章一开始,松哥列出来的示例代码中,HttpSecurity 中其实就是在配置各种各样的 xxxConfigurer。

SecurityConfigurer 有一个重要的实现类就是 SecurityConfigurerAdapter,默认的 15 个过滤器的 Configurer 类都是继承自它!而在 SecurityConfigurerAdapter 中就多出来一个方法:

public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
implements SecurityConfigurer<O, B> {

public void init(B builder) throws Exception {
}

public void configure(B builder) throws Exception {
}
public B and() {
return getBuilder();
}

}

没错,就是大家所熟知的 and 方法。and 方法的返回值是一个 SecurityBuilder 的子类,其实就是 HttpSecurity,也就是 and 方法总是让我们回到 HttpSecurity,从而开启新一轮的 xxxConfigurer 配置。

我们再来瞅一眼 HttpSecurity 中到底都有啥方法(方法比较多,我这里仅列举一部分):

Spring Security 配置中的 and 到底该怎么理解?_spring_02

可以看到,每一个类型的配置,都有一个对应的返回 Configure 的方法,例如 OpenIDLoginConfigurer、HeadersConfigurer、CorsConfigurer 等等,大家注意,每一个 configure 方法都有一个 HttpSecurity 作为泛型,这实际上就指定了 and 方法的返回类型。

我再举个例子,大家可能更清楚一些,以 HttpSecurity 中 RememberME 的配置为例,有两个方法:


  • RememberMeConfigurer rememberMe()
  • HttpSecurity rememberMe(Customizer<RememberMeConfigurer> rememberMeCustomizer)


  1. 第一个 rememberMe 方法没有参数,但是返回值是一个 RememberMeConfigurer,我们可以在这个 RememberMeConfigurer 上继续配置 RememberME 相关的其他属性,配置完成后,通过 and 方法重新回到 HttpSecurity 对象,松哥前面文章基本上都是采用这种方式配置的,这里我就不重复举例子了。
  2. 第二个 rememberMe 方法有参数,参数是一个 Customizer ,但是带着一个 RememberMeConfigurer 泛型。其实 Customizer 就是一个接口,我们可以通过匿名内部类的方式来实现该接口,这个接口中就一个实例方法,而且该方法的参数还是你传入的泛型,即 RememberMeConfigurer,其实也就是我们换了个地方去配置 RememberMeConfigurer 了,配置完成后,这个方法会直接返回 HttpSecurity,此时就不再需要 and 方法了。配置示例如下(注意配置完成后不需要 and 方法就能继续后面的配置了):

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.rememberMe(new Customizer<RememberMeConfigurer<HttpSecurity>>() {
@Override
public void customize(RememberMeConfigurer<HttpSecurity> httpSecurityRememberMeConfigurer) {
httpSecurityRememberMeConfigurer.key("123");
}
})
.csrf().disable();
}

这就是我们在 configure(HttpSecurity http) 方法中的配置过程。

3.小结

通过前面的讲解,不知道小伙伴们有没有看懂呢?我再给大家总结下。

Spring Security 的功能主要是通过各种各样的过滤器来实现的,各种各样的过滤器都由对应的 xxxConfigurer 来进行配置,我们在 configure(HttpSecurity http) 中所做的配置其实就是在配置 xxxConfigurer,也是在间接的配置过滤器,每一个 and 方法会将我们带回到 HttpSecurity 实例中,从而开启新一轮的配置。

大致就是这样!文章案例下载地址:​​https://github.com/lenve/spring-security-samples​

小伙伴们如果觉得有收获