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 来忽略请求,建议优先考虑通过配置 HttpSecurityauthorizeHttpRequests() 方法,使用 .permitAll() 来实现。

四、配置LDAP认证

LDAP (Light Directory Access Protocol) ,是基于 X.500 标准的轻量级目录访问协议。有兴趣的同学可以自行谷歌搜索学习,LDAP 这种协议常用于授权认证的情景。

在 Spring Security 5.7 中,Spring 官方新增了 EmbeddedLdapServerContextSourceFactoryBeanLdapBindAuthenticationManagerFactoryLdapPasswordComparisonAuthenticationManagerFactory ,用来创建一个嵌入式 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

标签:Spring,Security,配置
0
投稿

猜你喜欢

  • 使用Spring特性实现接口多实现类的动态调用方式

    2022-04-11 05:41:42
  • Java利用openoffice将doc、docx转为pdf实例代码

    2023-08-07 23:44:34
  • Android实现无标题栏全屏的方法

    2023-06-25 11:14:27
  • Android中三种注入事件方法比较

    2022-11-20 18:17:23
  • Android实现关机与重启的几种方式(推荐)

    2022-08-31 14:29:54
  • Android 高仿微信朋友圈动态支持双击手势放大并滑动查看图片效果

    2021-08-21 21:16:40
  • C#调用存储过程详解(带返回值、参数输入输出等)

    2023-12-06 01:06:35
  • c#使用linq把多列的List转化为只有指定列的List

    2022-07-04 12:00:31
  • ArrayList和LinkedList的区别、扩容机制以及底层的实现方式

    2023-11-27 01:26:57
  • Mybatis实现数据的增删改查实例(CRUD)

    2022-05-29 07:03:50
  • Idea打包springboot项目没有.original文件解决方案

    2022-07-12 20:06:08
  • Android实现微信朋友圈发本地视频功能

    2021-06-18 07:56:03
  • Android控件之TabHost用法实例分析

    2021-07-19 10:35:21
  • Android中AOP的应用实践之过滤重复点击

    2022-09-19 22:01:02
  • Jmeter接口登录获取参数token报错问题解决方案

    2021-09-13 10:34:31
  • 在Android环境下WebView中拦截所有请求并替换URL示例详解

    2022-05-26 06:21:56
  • Java 两种延时thread和timer详解及实例代码

    2022-08-30 03:42:14
  • c#中返回文章发表的时间差的示例

    2021-08-18 16:51:26
  • 简单谈谈SpringMVC转发和重定向的区别

    2023-03-12 11:07:52
  • Java使用WatchService监控文件内容变化的示例

    2023-02-15 10:10:19
  • asp之家 软件编程 m.aspxhome.com