Spring?Security配置保姆級(jí)教程
背景
筆者使用 Spring Security 5.8 時(shí),發(fā)現(xiàn)網(wǎng)上很多教程所教的 Spring Security 配置類(lèi) SecurityConfig.java
的配置風(fēng)格還是停留在繼承 WebSecurityConfigurerAdapter
的風(fēng)格。然而, WebSecurityConfigurerAdapter
在 Spring Security 5.7.0-M2 版本中已經(jīng)被 deprecated 了。因此在本文中分享 Spring 官方最新推薦的 Spring Security 配置風(fēng)格。
一、前言
在 Spring Security 5.7.0 (2022 年 2 月 21 日更新) 中,官方棄用了 WebSecurityConfigurerAdapter
。因?yàn)镾pring 官方鼓勵(lì)開(kāi)發(fā)者朝著組件化安全配置遷移。為了幫助開(kāi)發(fā)者順利過(guò)渡到這種配置風(fēng)格,Spring 官方準(zhǔn)備了一系列常見(jiàn)的使用案例和建議的替代方案。
在下面的例子中,我們將遵循最佳實(shí)踐,使用 Spring Security lambda DSL 和 HttpSecurity.java
中的 authorizeHttpRequests()
方法來(lái)定義授權(quán)規(guī)則。如果您對(duì) lambda DSL 感到陌生,可以參考我的這篇文章進(jìn)行學(xué)習(xí):《如何使用Lambda DSL配置Spring Security》。
二、配置HttpSecurity
在 Spring Security 5.4 中,新增了通過(guò)創(chuàng)建一個(gè) SecurityFilterChain
的 Bean 來(lái)配置 HttpSecurity
的功能。
首先來(lái)看看沒(méi)有棄用 WebSecurityConfigurerAdapter
的示例,下面是使用 WebSecurityConfigurerAdapter
的配置示例,該配置使用 httpBasic()
方法來(lái)保護(hù)所有的接口。
@Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeHttpRequests((authz) -> authz .anyRequest().authenticated() ) .httpBasic(withDefaults()); } }
但今后, WebSecurityConfigurerAdapter
就被 Spring 官方棄用了,取而代之的是通過(guò)注冊(cè)一個(gè) SecurityFilterChain
的 Bean 來(lái)配置 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
是一個(gè)回調(diào)接口,可以用來(lái)配置 WebSecurity
。
首先來(lái)看看先前的舊配置風(fēng)格的代碼示例:使用 WebSecurityConfigurerAdapter
來(lái)忽略與 /ignore1
或 /ignore2
匹配的請(qǐng)求 (即不攔截這兩個(gè)請(qǐng)求進(jìn)行認(rèn)證授權(quán)) 。
@Configuration public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) { web.ignoring().antMatchers("/ignore1", "/ignore2"); } }
但今后,Spring 官方不再推薦上面的配置風(fēng)格,取而代之的是向 Spring 容器中注入一個(gè) WebSecurityCustomizer
的 Bean 。
@Configuration public class SecurityConfiguration { @Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2"); } }
注意:如果你想通過(guò)配置 WebSecurity
來(lái)忽略請(qǐng)求,建議優(yōu)先考慮通過(guò)配置 HttpSecurity
的 authorizeHttpRequests()
方法,使用 .permitAll()
來(lái)實(shí)現(xiàn)。
四、配置LDAP認(rèn)證
LDAP (Light Directory Access Protocol) ,是基于 X.500 標(biāo)準(zhǔn)的輕量級(jí)目錄訪問(wèn)協(xié)議。有興趣的同學(xué)可以自行谷歌搜索學(xué)習(xí),LDAP 這種協(xié)議常用于授權(quán)認(rèn)證的情景。
在 Spring Security 5.7 中,Spring 官方新增了 EmbeddedLdapServerContextSourceFactoryBean
、LdapBindAuthenticationManagerFactory
和 LdapPasswordComparisonAuthenticationManagerFactory
,用來(lái)創(chuàng)建一個(gè)嵌入式 LDAP 服務(wù)器和一個(gè) AuthenticationManager
對(duì)象,來(lái)執(zhí)行 LDAP 認(rèn)證。
同樣的,先來(lái)看先前舊的配置風(fēng)格的示例,繼承 WebSecurityConfigurerAdapter
,創(chuàng)建一個(gè)嵌入式 LDAP 服務(wù)器,創(chuàng)建一個(gè) AuthenticationManager
,使用綁定認(rèn)證 (Bind Authentication) 來(lái)執(zhí)行 LDAP 認(rèn)證。
@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 官方不再推薦上面的配置風(fēng)格,取而代之的是使用新的 LDAP 類(lèi)。
@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認(rèn)證
同樣的,先來(lái)看先前舊的配置風(fēng)格的示例,繼承 WebSecurityConfigurerAdapter
,使用一個(gè)以默認(rèn)方式初始化且具有一個(gè)用戶的嵌入式 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 官方不再推薦上面的配置風(fēng)格,取而代之的是向 Spring 容器注入一個(gè) 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()
。在實(shí)際的生產(chǎn)環(huán)境中并不會(huì)使用默認(rèn)的密碼編碼器,因?yàn)檫@樣存儲(chǔ)的密碼是明文,十分不安全。這里推薦使用 BCryptPasswordEncoder 作為密碼編碼器來(lái)對(duì)密碼進(jìn)行加密成暗文存儲(chǔ)。
六、In-Memory Authentication
與上面存儲(chǔ)在數(shù)據(jù)庫(kù)的不同,In-Memory 是把用戶信息存儲(chǔ)在內(nèi)存中。
同樣的,先來(lái)看先前舊的配置風(fēng)格的示例,繼承 WebSecurityConfigurerAdapter
來(lái)配置存儲(chǔ)在內(nèi)存中的一個(gè)用戶信息。
@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 官方不再推薦上面的配置風(fēng)格,取而代之的是向 Spring 容器注入一個(gè) 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()
。在實(shí)際的生產(chǎn)環(huán)境中并不會(huì)使用默認(rèn)的密碼編碼器,因?yàn)檫@樣存儲(chǔ)的密碼是明文,十分不安全。這里推薦使用 BCryptPasswordEncoder 作為密碼編碼器來(lái)對(duì)密碼進(jìn)行加密成暗文存儲(chǔ)。
七、配置全局AuthenticationManager
如果你想要?jiǎng)?chuàng)建整個(gè)應(yīng)用都可以調(diào)用的 AuthenticationManager
對(duì)象,只需要簡(jiǎn)單地使用注解 @Bean
來(lái)注入 Spring 容器。
配置風(fēng)格示例與 LDAP 的配置示例相同。
八、配置局部AuthenticationManager
在 Spring Security 5.6 中,Spring 官方在 HttpSecurity
中新增了方法 authenticationManager()
,該方法重寫(xiě)具體的 SecurityFilterChain
的默認(rèn) 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(); } }
九、調(diào)用局部AuthenticationManager
可以使用 custom DSL 來(lái)調(diào)用局部 AuthenticationManager
。這實(shí)際上正是 Spring Security 內(nèi)部實(shí)現(xiàn)諸如 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(); } }
當(dāng)Spring Security開(kāi)始構(gòu)建 SecurityFilterChain
時(shí),custom DSL 就會(huì)被自動(dòng)調(diào)用。
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // ... http.apply(customDsl()); return http.build(); }
到此這篇關(guān)于Spring Security配置保姆級(jí)教程的文章就介紹到這了,更多相關(guān)Spring Security配置內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redisson RedLock紅鎖加鎖實(shí)現(xiàn)過(guò)程及原理
本文主要介紹了Redis中Redisson紅鎖(Redlock)使用原理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02分布式調(diào)度器之Spring Task 的使用詳解
SpringTask是Spring框架中用于任務(wù)調(diào)度的組件,通過(guò)簡(jiǎn)單的注解就能實(shí)現(xiàn)定時(shí)任務(wù)的創(chuàng)建和調(diào)度,可以通過(guò)配置線程池來(lái)實(shí)現(xiàn),本文給大家介紹分布式調(diào)度器之Spring Task 的使用,感興趣的朋友跟隨小編一起看看吧2024-10-10Spring boot連接MySQL 8.0可能出現(xiàn)的問(wèn)題
這篇文章主要給大家介紹了關(guān)于Spring boot連接MySQL 8.0可能出現(xiàn)的問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-10-10關(guān)于Java Spring三級(jí)緩存和循環(huán)依賴(lài)的深入理解
對(duì)于循環(huán)依賴(lài),我相信讀者無(wú)論只是聽(tīng)過(guò)也好,還是有過(guò)了解也好,至少都有所接觸。但是我發(fā)現(xiàn)目前許多博客對(duì)于循環(huán)依賴(lài)的講解并不清楚,都提到了Spring的循環(huán)依賴(lài)解決方案是三級(jí)緩存,但是三級(jí)緩存每一級(jí)的作用是什么,很多博客都沒(méi)有提到,本篇文章帶你深入了解2021-09-09SpringBoot集成mqtt的多模塊項(xiàng)目配置詳解
這篇文章主要介紹了SpringBoot集成mqtt的多模塊項(xiàng)目配置詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Java多例Bean的應(yīng)用場(chǎng)景-easyExcel導(dǎo)入
EasyExcel 是一個(gè)基于 Java 的簡(jiǎn)單、省內(nèi)存的讀寫(xiě) Excel 的開(kāi)源項(xiàng)目。這篇文章主要介紹了用easyExcel導(dǎo)入Java Bean的應(yīng)用場(chǎng)景,感興趣的朋友可以參考閱讀2023-04-04SpringBoot是如何使用SQL數(shù)據(jù)庫(kù)的?
今天給大家?guī)?lái)的是關(guān)于Springboot的相關(guān)知識(shí),文章圍繞著SpringBoot是如何使用SQL數(shù)據(jù)庫(kù)的展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06Java的String類(lèi)中的startsWith方法和endsWith方法示例詳解
大家應(yīng)該都知道startsWith()方法用于檢測(cè)字符串是否以指定的前綴開(kāi)始,endsWith()方法用于測(cè)試字符串是否以指定的后綴結(jié)束,本文就Java的String類(lèi)中的startsWith方法和endsWith方法給大家詳細(xì)講解,感興趣的朋友一起看看吧2023-11-11