没有WebSecurityConfigurerAdapter的Spring Security
没有WebSecurityConfigurerAdapter的Spring Security
最近一直在从springboot2.x向3.x学习迁移,付出了很大的学习成本,心得会不断分享。对于spring security来说,是该和WebSecurityConfigurerAdapter说再见了,因为在Spring Security 5.7.0-M2中WebSecurityConfigurerAdapter已经被弃用找不到了。搬来了官方的博文,翻译不好的地方可以看原文,链接如下:
Spring Security without the WebSecurityConfigurerAdapter
在Spring Security 5.7.0-M2中,我们弃用了WebSecurityConfigurerAdapter,因为我们鼓励用户转向基于组件的安全配置。
为了帮助过渡到这种新的配置风格,我们编制了一份常见用例和建议替代方案的列表。
在下面的示例中,我们遵循最佳实践,使用Spring Security lambda DSL和方法httpsecurity# authorizeHttpRequests来定义授权规则。如果您是lambda DSL的新手,您可以在这篇博客文章中了解它。如果你想了解更多关于为什么我们选择使用httpsecurity# authorizeHttpRequests,你可以查看参考文档(Authorize HttpServletRequests :: Spring Security)。
配置HttpSecurity
在Spring Security 5.4中,我们引入了通过创建SecurityFilterChain bean来配置HttpSecurity的功能。
下面是一个使用WebSecurityConfigurerAdapter的配置示例,它使用HTTP Basic保护所有端点:
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
}
}
接下来,推荐的方法是注册一个SecurityFilterChain bean:
@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中,我们还引入了WebSecurityCustomizer。
WebSecurityCustomizer是一个回调接口,可用于自定义WebSecurity。
下面是一个使用WebSecurityConfigurerAdapter忽略匹配/ignore1或/ignore2的请求的配置示例:
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/ignore1", "/ignore2");
}
}
接下来,推荐的方法是注册一个WebSecurityCustomizer bean:
@Configuration
public class SecurityConfiguration {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2");
}
}
警告:如果你将WebSecurity配置为忽略请求,请考虑通过httpsecurity# authorizeHttpRequests使用permitAll。有关其他详细信息,请参阅configure Javadoc。
LDAP身份验证
在Spring Security 5.7中,我们介绍了EmbeddedLdapServerContextSourceFactoryBean、LdapBindAuthenticationManagerFactory和LdapPasswordComparisonAuthenticationManagerFactory,它们可用于创建嵌入式LDAP服务器和执行LDAP身份验证的AuthenticationManager。
下面是一个使用WebSecurityConfigurerAdapter的配置示例,它创建了一个嵌入式LDAP服务器和一个使用绑定身份验证执行LDAP身份验证的AuthenticationManager:
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDetailsContextMapper(new PersonContextMapper())
.userDnPatterns("uid={0},ou=people")
.contextSource()
.port(0);
}
}
接下来,推荐的方法是使用新的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的配置示例,其中嵌入了一个用默认模式初始化的数据源,并且只有一个用户:
@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);
}
}
推荐的方法是注册一个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;
}
}
注意:在这些示例中,为了提高可读性,我们使用了User.withDefaultPasswordEncoder()方法。它不是用于生产环境的,相反,我们建议在外部散列您的密码。一种方法是使用参考文档中描述的Spring Boot CLI。
内存中的身份验证
下面是一个使用WebSecurityConfigurerAdapter的配置示例,它用单个用户配置内存中的用户存储:
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
auth.inMemoryAuthentication()
.withUser(user);
}
}
推荐的方法是注册一个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);
}
}
注意:在这些示例中,为了提高可读性,我们使用了User.withDefaultPasswordEncoder()方法。它不是用于生产环境的,相反,我们建议在外部散列您的密码。一种方法是使用参考文档中描述的Spring Boot CLI。
全局AuthenticationManager
要创建一个对整个应用程序可用的AuthenticationManager,只需将AuthenticationManager注册为@Bean即可。
在上面的LDAP身份验证示例中显示了这种类型的配置。
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter#ldap-authentication
本地AuthenticationManager
在Spring Security 5.6中,我们引入了httpsecurity# authenticationManager方法,它覆盖了特定SecurityFilterChain的默认authenticationManager。下面是一个将自定义AuthenticationManager设置为默认值的示例配置:
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults())
.authenticationManager(new CustomAuthenticationManager());
return http.build();
}
}
访问本地AuthenticationManager
本地AuthenticationManager可以在自定义DSL中访问。这实际上是Spring Security内部实现HttpSecurity.authorizeRequests()等方法的方式。
public class MyCustomDsl extends AbstractHttpConfigurer {
@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();
}
}
自定义DSL可以在构建SecurityFilterChain时应用:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ...
http.apply(customDsl());
return http.build();
}