我们先来看一个简单的配置:
@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 到底怎么玩!
本文是 Spring Security 系列第 33 篇,阅读前面文章有助于更好的理解本文:
- 挖一个大坑,Spring Security 开搞!
- 松哥手把手带你入门 Spring Security,别再问密码怎么解密了
- 手把手教你定制 Spring Security 中的表单登录
- Spring Security 做前后端分离,咱就别做页面跳转了!统统 JSON 交互
- Spring Security 中的授权操作原来这么简单
- Spring Security 如何将用户数据存入数据库?
- Spring Security+Spring Data Jpa 强强联手,安全管理只有更简单!
- Spring Boot + Spring Security 实现自动登录功能
- Spring Boot 自动登录,安全风险要怎么控制?
- 在微服务项目中,Spring Security 比 Shiro 强在哪?
- SpringSecurity 自定义认证逻辑的两种方式(高级玩法)
- Spring Security 中如何快速查看登录用户 IP 地址等信息?
- Spring Security 自动踢掉前一个登录用户,一个配置搞定!
- Spring Boot + Vue 前后端分离项目,如何踢掉已登录用户?
- Spring Security 自带防火墙!你都不知道自己的系统有多安全!
- 什么是会话固定攻击?Spring Boot 中要如何防御会话固定攻击?
- 集群化部署,Spring Security 要如何处理 session 共享?
- 松哥手把手教你在 SpringBoot 中防御 CSRF 攻击!so easy!
- 要学就学透彻!Spring Security 中 CSRF 防御源码解析
- Spring Boot 中密码加密的两种姿势!
- Spring Security 要怎么学?为什么一定要成体系的学习?
- Spring Security 两种资源放行策略,千万别用错了!
- 松哥手把手教你入门 Spring Boot + CAS 单点登录
- Spring Boot 实现单点登录的第三种方案!
- Spring Boot+CAS 单点登录,如何对接数据库?
- Spring Boot+CAS 默认登录页面太丑了,怎么办?
- 用 Swagger 测试接口,怎么在请求头中携带 Token?
- Spring Boot 中三种跨域场景总结
- Spring Boot 中如何实现 HTTP 认证?
- Spring Security 中的四种权限控制方式
- Spring Security 多种加密方案共存,老破旧系统整合利器!
- 神奇!自己 new 出来的对象一样也可以被 Spring 容器管理!
# 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 大家稍微瞅一眼大概就能明白其中的含义:
- intercept-url 相当于配置拦截规则
- form-login 是配置表单登录
- http-basic 是配置 HttpBasic 认证
如果我们使用了 Java 配置,这些 XML 配置都有对应的写法,例如 .anyRequest().authenticated()
就是配置拦截规则的,.formLogin()
是配置表单登录细节的。
仅仅从语义层面来理解,and 有点类似于 XML 中的结束标签,每当 and 出现,当前的配置项就结束了,可以开启下一个配置了。
那么从代码层面上,这个要如何理解呢?
# 2.代码层面的理解
小伙伴们知道,Spring Security 中的功能是由一系列的过滤器来实现的,默认的过滤器一共有 15 个,这 15 个过滤器松哥以后会和大家挨个介绍。
每一个过滤器都有一个对应的 configurer 来对其进行配置,例如我们常见的 UsernamePasswordAuthenticationFilter 过滤器就是通过 AbstractAuthenticationFilterConfigurer 来进行配置的。
这些 configure 都有一个共同的父类,那就是 SecurityConfigurer,给大家大致看一下 SecurityConfigurer 的继承关系图:
可以看到,它的实现类还是蛮多的。
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 中到底都有啥方法(方法比较多,我这里仅列举一部分):
可以看到,每一个类型的配置,都有一个对应的返回 Configure 的方法,例如 OpenIDLoginConfigurer、HeadersConfigurer、CorsConfigurer 等等,大家注意,每一个 configure 方法都有一个 HttpSecurity 作为泛型,这实际上就指定了 and 方法的返回类型。
我再举个例子,大家可能更清楚一些,以 HttpSecurity 中 RememberME 的配置为例,有两个方法:
- RememberMeConfigurer
rememberMe() - HttpSecurity rememberMe(Customizer<RememberMeConfigurer
> rememberMeCustomizer)
- 第一个 rememberMe 方法没有参数,但是返回值是一个 RememberMeConfigurer,我们可以在这个 RememberMeConfigurer 上继续配置 RememberME 相关的其他属性,配置完成后,通过 and 方法重新回到 HttpSecurity 对象,松哥前面文章基本上都是采用这种方式配置的,这里我就不重复举例子了。
- 第二个 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 实例中,从而开启新一轮的配置。
大致就是这样!
小伙伴们如果觉得有收获,记得点个在看鼓励下松哥哦~