本系列文章參考了大量文獻資料,如楊傳傑博客,江南一點雨等,感謝原作者。
本文默認版本:SpringBoot 2.1.9
概述
Spring Security 是 Spring 社區的一個頂級項目,也是 Spring Boot 官方推薦使用的安全框架。除了常規的認證(Authentication)和授權(Authorization)之外,Spring Security還提供了諸如ACLs,LDAP,JAAS,CAS等高級特性以滿足複雜場景下的安全需求。另外,就目前而言,Spring Security和Shiro也是當前廣大應用使用比較廣泛的兩個安全框架。
Spring Security 應用級別的安全主要包含兩個主要部分,即登錄認證(Authentication)和訪問授權(Authorization),首先用戶登錄的時候傳入登錄信息,登錄驗證器完成登錄認證並將登錄認證好的信息存儲到請求上下文,然後再進行其他操作,如在進行接口訪問、方法調用時,權限認證器從上下文中獲取登錄認證信息,然後根據認證信息獲取權限信息,通過權限信息和特定的授權策略決定是否授權。
實際上,在 Spring Boot 出現之前,Spring Security 就已經發展了多年了,但是使用的並不多,安全管理這個領域,一直是 Shiro 的天下。相對於 Shiro,在 SSM/SSH 中整合 Spring Security 都是比較麻煩的操作,所以,Spring Security 雖然功能比 Shiro 強大,但是使用反而沒有 Shiro 多(Shiro 雖然功能沒有 Spring Security 多,但是對於大部分項目而言,Shiro 也夠用了)。
自從有了 Spring Boot 之後,Spring Boot 對於 Spring Security 提供了 自動化配置方案,可以零配置使用 Spring Security。
因此,一般來說,常見的安全管理技術棧的組合是這樣的:
- SSM + Shiro
- Spring Boot/Spring Cloud + Spring Security
注意,這只是一個推薦的組合而已,如果單純從技術上來說,無論怎麼組合,都是可以運行的。
一、創建一個SpringBoot項目,引入SpringSecurity依賴
- pom.xml 中的 Spring Security 依賴:
<!-- spring security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
只要加入依賴,項目的所有接口都會被自動保護起來。
注意:引入spring security依賴時,會自動啓動spring security
禁用:@SpringBootApplication(exclude = {SecurityAutoConfiguration.class })
-
我們創建一個 HelloController:
/** * @author ZHANGCHAO * @date 2020/3/11 10:34 * @since 1.0.0 */ @RestController public class MainController { @GetMapping("/hello") public String login() { return "Hello World"; } }
啓動項目–觀察控制檯
默認情況下,登錄的用戶名是
user
,密碼則是項目啓動時隨機生成的字符串,可以從啓動的控制檯日誌中看到默認密碼。訪問
/hello
,會看到spring security使用了默認登錄頁面,默認用戶user,密碼就是上面的,輸入用戶密碼成功訪問。當用戶從瀏覽器發送請求訪問
/hello
接口時,服務端會返回302
響應碼,讓客戶端重定向到/login
頁面,用戶在/login
頁面登錄,登陸成功之後,就會自動跳轉到/hello
接口。另外,也可以使用
POSTMAN
來發送請求,使用POSTMAN
發送請求時,可以將用戶信息放在請求頭中(這樣可以避免重定向到登錄頁面):
通過以上兩種不同的登錄方式,可以看出,Spring Security 支持兩種不同的認證方式:
- 可以通過 form 表單來認證
- 可以通過 HttpBasic 來認證
二、自定義登錄用戶和密碼
隨機生成的密碼,每次啓動時都會變。對登錄的用戶名/密碼進行配置,有三種不同的方式:
- 在 application.properties 中進行配置
- 通過 Java 代碼配置在內存中
- 通過 Java 從數據庫中加載
-
直接在 application.properties 文件中配置用戶的基本信息:
## Spring Security配置 spring.security.user.name=admin spring.security.user.password=666666
配置完成後,重啓項目,就可以使用這裏配置的用戶名/密碼登錄了。
-
在 Java 代碼中配置用戶名密碼,首先需要我們創建一個 Spring Security 的配置類,集成自 WebSecurityConfigurerAdapter 類,如下:
@Slf4j @EnableWebSecurity //開啓web security配置 這是一個組合註解 public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * spring5.0之後,spring security必須設置加密方法否則會報 * There is no PasswordEncoder mapped for the id "null" * @return 加密 */ @Bean public BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(4); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() //需要授權的請求 .anyRequest().authenticated() //對任何一個請求,都需要認證 .and() //完成上一個配置,進行下一步配置 .httpBasic(); //開啓httpBasic登錄 } }
從 Spring5 開始,強制要求密碼要加密,如果非不想加密,可以使用一個過期的 PasswordEncoder 的實例 NoOpPasswordEncoder,但是不建議這麼做,畢竟不安全。
-
配置AuthenticationManager
有兩種方式:
(1) 重寫configure(AuthenticationManagerBuilder auth)
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { super.configure(auth); }
(2) 注入 configureGlobal(AuthenticationManagerBuilder auth)
@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {}
說明: configureGlobal可以跨多個WebSecurityConfigurerAdapter;configure只能只能作用一個!
如果你的應用只有唯一一個WebSecurityConfigurerAdapter,那麼他們之間的差距可以被忽略,從方法名可以看出兩者的區別:使用@Autowired注入的AuthenticationManagerBuilder是全局的身份認證器,作用域可以跨越多個WebSecurityConfigurerAdapter,以及影響到基於Method的安全控制;而
protected configure()
的方式則類似於一個匿名內部類,它的作用域侷限於一個WebSecurityConfigurerAdapter內部。最後啓動應用,輸入admin,666666成功通過驗證。
三、自定義登錄頁面和退出頁面
-
配置WebMvcConfig
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/home").setViewName("home"); registry.addViewController("/main").setViewName("main"); registry.addViewController("/").setViewName("home"); registry.addViewController("/hello").setViewName("hello"); registry.addViewController("/login").setViewName("login"); }
當項目中涉及大量的頁面跳轉,我們可以使用addViewControllers方法實現無業務邏輯跳轉,從而減少控制器代碼的編寫。
addViewControllers方法可以實現將一個請求直接映射爲視圖,不需要編寫控制器來實現,從而簡化了頁面跳轉。
-
在SecurityConfig中配置HttpSecurity
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() //需要授權的請求 .antMatchers("/login","/home").permitAll() //過濾不需要認證的路徑 .anyRequest().authenticated() //對任何一個請求,都需要認證 .and() //完成上一個配置,進行下一步配置 //.httpBasic(); .formLogin() //配置表單登錄 .loginPage("/login") //設置登錄頁面 .and() .logout() //登出 .logoutSuccessUrl("/home"); //設置退出頁面 }
-
在resources下新建目錄templates,新建幾個頁面