Spring Security配置保姆级教程
作者:自牧君 发布时间:2023-11-07 11:46:56
背景
笔者使用 Spring Security 5.8 时,发现网上很多教程所教的 Spring Security 配置类 SecurityConfig.java
的配置风格还是停留在继承 WebSecurityConfigurerAdapter
的风格。然而, WebSecurityConfigurerAdapter
在 Spring Security 5.7.0-M2 版本中已经被 deprecated 了。因此在本文中分享 Spring 官方最新推荐的 Spring Security 配置风格。
一、前言
在 Spring Security 5.7.0 (2022 年 2 月 21 日更新) 中,官方弃用了 WebSecurityConfigurerAdapter
。因为Spring 官方鼓励开发者朝着组件化安全配置迁移。为了帮助开发者顺利过渡到这种配置风格,Spring 官方准备了一系列常见的使用案例和建议的替代方案。
在下面的例子中,我们将遵循最佳实践,使用 Spring Security lambda DSL 和 HttpSecurity.java
中的 authorizeHttpRequests()
方法来定义授权规则。如果您对 lambda DSL 感到陌生,可以参考我的这篇文章进行学习:《如何使用Lambda DSL配置Spring Security》。
二、配置HttpSecurity
在 Spring Security 5.4 中,新增了通过创建一个 SecurityFilterChain
的 Bean 来配置 HttpSecurity
的功能。
首先来看看没有弃用 WebSecurityConfigurerAdapter
的示例,下面是使用 WebSecurityConfigurerAdapter
的配置示例,该配置使用 httpBasic()
方法来保护所有的接口。
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
}
}
但今后, WebSecurityConfigurerAdapter
就被 Spring 官方弃用了,取而代之的是通过注册一个 SecurityFilterChain
的 Bean 来配置 HttpSecurity
。
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
return http.build();
}
}
三、配置WebSecurity
在 Spring Security 5.4 中,Spring 官方新增了 WebSecurityCustomizer
。
WebSecurityCustomizer
是一个回调接口,可以用来配置 WebSecurity
。
首先来看看先前的旧配置风格的代码示例:使用 WebSecurityConfigurerAdapter
来忽略与 /ignore1
或 /ignore2
匹配的请求 (即不拦截这两个请求进行认证授权) 。
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/ignore1", "/ignore2");
}
}
但今后,Spring 官方不再推荐上面的配置风格,取而代之的是向 Spring 容器中注入一个 WebSecurityCustomizer
的 Bean 。
@Configuration
public class SecurityConfiguration {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2");
}
}
注意:如果你想通过配置 WebSecurity
来忽略请求,建议优先考虑通过配置 HttpSecurity
的 authorizeHttpRequests()
方法,使用 .permitAll()
来实现。
四、配置LDAP认证
LDAP (Light Directory Access Protocol) ,是基于 X.500 标准的轻量级目录访问协议。有兴趣的同学可以自行谷歌搜索学习,LDAP 这种协议常用于授权认证的情景。
在 Spring Security 5.7 中,Spring 官方新增了 EmbeddedLdapServerContextSourceFactoryBean
、LdapBindAuthenticationManagerFactory
和 LdapPasswordComparisonAuthenticationManagerFactory
,用来创建一个嵌入式 LDAP 服务器和一个 AuthenticationManager
对象,来执行 LDAP 认证。
同样的,先来看先前旧的配置风格的示例,继承 WebSecurityConfigurerAdapter
,创建一个嵌入式 LDAP 服务器,创建一个 AuthenticationManager
,使用绑定认证 (Bind Authentication) 来执行 LDAP 认证。
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ladpAuthentication()
.userDetailsContextMapper(new PersonContextMapper())
.userDnPatterns("uid={0}, ou=people")
.contextSource()
.port(0);
}
}
但今后,Spring 官方不再推荐上面的配置风格,取而代之的是使用新的 LDAP 类。
@Configuration
public class SecurityConfiguration {
@Bean
public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean =
EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();
contextSourceFactoryBean.setPort(0);
return contextSourceFactoryBean;
}
@Bean
AuthenticationManager ldapAuthenticationManager(
BaseLdapPathContextSource contextSource) {
LdapBindAuthenticationManagerFactory factory =
new LdapBindAuthenticationManagerFactory(contextSource);
factory.setUserDnPatterns("uid={0}, ou=people");
factory.setUserDetailsContextMapper(new PersonContextMapper());
return factory.createAuthenticationManager();
}
}
五、配置JDBC认证
同样的,先来看先前旧的配置风格的示例,继承 WebSecurityConfigurerAdapter
,使用一个以默认方式初始化且具有一个用户的嵌入式 DataSource
。
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
auth.jdbcAuthentication()
.withDefaultSchema()
.dataSource(dataSource())
.withUser(user);
}
}
但今后,Spring 官方不再推荐上面的配置风格,取而代之的是向 Spring 容器注入一个 JdbcUserDetailsManager
的 Bean 。
@Configuration
public class SecurityConfiguration {
@Bean
public DataSource datasource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
.build();
}
@Bean
public UserDetailsManager users(DataSource dataSource) {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource);
users.createUser(user);
return users;
}
}
注意:上面的代码示例第 14 行中,为了密码的可读性才使用方法 User.withDefaultPasswordEncoder()
。在实际的生产环境中并不会使用默认的密码编码器,因为这样存储的密码是明文,十分不安全。这里推荐使用 BCryptPasswordEncoder 作为密码编码器来对密码进行加密成暗文存储。
六、In-Memory Authentication
与上面存储在数据库的不同,In-Memory 是把用户信息存储在内存中。
同样的,先来看先前旧的配置风格的示例,继承 WebSecurityConfigurerAdapter
来配置存储在内存中的一个用户信息。
@Configuration
public class SecurityConfiguration extends WebSecutiryConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
auth.inMemoryAuthentication()
.withUser(user);
}
}
但今后,Spring 官方不再推荐上面的配置风格,取而代之的是向 Spring 容器注入一个 InMemoryUserDetailsManager
的 Bean 。
@Configuration
public class SecurityConfiguration {
@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
注意:上面的代码示例第 6 行中,为了密码的可读性才使用方法 User.withDefaultPasswordEncoder()
。在实际的生产环境中并不会使用默认的密码编码器,因为这样存储的密码是明文,十分不安全。这里推荐使用 BCryptPasswordEncoder 作为密码编码器来对密码进行加密成暗文存储。
七、配置全局AuthenticationManager
如果你想要创建整个应用都可以调用的 AuthenticationManager
对象,只需要简单地使用注解 @Bean
来注入 Spring 容器。
配置风格示例与 LDAP 的配置示例相同。
八、配置局部AuthenticationManager
在 Spring Security 5.6 中,Spring 官方在 HttpSecurity
中新增了方法 authenticationManager()
,该方法重写具体的 SecurityFilterChain
的默认 AuthenticationManager
。
下面是配置自定义局部 AuthenticationManager
的代码示例。
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authticated())
.httpBasic(withDefaults())
.authenticationManager(new CustomAuthenticationManager());
return http.build();
}
}
九、调用局部AuthenticationManager
可以使用 custom DSL 来调用局部 AuthenticationManager
。这实际上正是 Spring Security 内部实现诸如 HttpSecurity.authorizeRequests()
方法的方式。
public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
@Override
public void configure(HttpSecurity http) throws Exception {
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.addFilter(new CustomFilter(authenticationManager));
}
public static MyCustomDsl customDsl() {
return new MyCustomDsl();
}
}
当Spring Security开始构建 SecurityFilterChain
时,custom DSL 就会被自动调用。
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
http.apply(customDsl());
return http.build();
}
来源:https://blog.csdn.net/Sihang_Xie/article/details/128754411


猜你喜欢
- 我们平时使用的一些常见队列都是非阻塞队列,比如PriorityQueue、LinkedList(LinkedList是双向链表,它实现了De
- 1、Java版package com.lyz.utils.common; import java.io.UnsupportedEncodin
- createcriteria和or的区别mybatis generator插件生成的example中,有createcriteria和or方
- 在Ubuntu Android简单介绍硬件抽象层(HAL)一文中,
- 本文实例为大家分享了OpenGL绘制Bezier曲线的具体代码,供大家参考,具体内容如下最近在看Francis S Hill ,Jr 和 S
- 本文实例为大家分享了Android自定义输入法软键盘的具体代码,供大家参考,具体内容如下1 功能描述触屏设备主界面中有一个文本编辑框,底部区
- 在程序开发过程中,LOG是广泛使用的用来记录程序执行过程的机制,它
- 给对象按照字符串属性进行排序在java中对象进行排序,排序的属性是string,我们只需要实现Comparator接口,然后实现比较的方式。
- 建造者模式是Java中一种创建型设计模式,它的主要目的是将一个复杂对象的构建过程分解为多个简单对象的构建过程,并且使这些构建过程按照一定的顺
- 一种可以设置滑动动画的控件,只显示一行布局,在布局文件中的ViewFlipper控件中顺序写好每一行的布局(1).MainActivity.
- 常用事件的分类Java AWT里面的事件可以简单的分为窗体事件(WindowEvent),鼠标事件(MouseEvent),键盘事件(Key
- 本文实例为大家分享了Android GestureDetector实现手势滑动的具体代码,供大家参考,具体内容如下目标效果: 程
- 这几天谷歌推出了as3.0的正式版,相信大家都进行更新了,然后对3.0的新特性也有过一些了解,最后磨刀霍霍开始宰杀,然鹅却一不小心就开始了排
- 在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户
- 单元测试是编写测试代码,应该准确、快速地保证程序基本模块的正确性。JUnit是Java单元测试框架,已经在Eclipse中默认安装。JUni
- 初次使用IDEA,创建一个maven工程,发现在目录结构中产生了两个不一样的东西——.iml文件和.idea文件夹。非常好奇,所以立刻上网查
- java使用贪心算法解决电台覆盖问题代码实现/** * 贪心算法实现集合覆盖 */public class Demo { &n
- 封装(Encapsulation)是面向对象编程的一个核心概念,它意味着将数据(属性)和方法(操作数据的函数)捆绑在一起,形成一个类(Cla
- 一、AQS介绍队列同步器AbstractQueuedSynchronizer(简称AQS),AQS定义了一套多线程访问共享资源的同步器框架,
- spring boot与profilespring boot 的项目中不再使用xml的方式进行配置,并且,它还遵循着约定大于配置。静态获取方