一、所需的組件
SpringBoot項目需要的POM依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> </dependency>
具體的版本號是跟隨你的Boot版本走的:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
其他常規依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency>
二、上手入門
在static目錄下編寫一個首頁:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Project-Index</title> </head> <body> <h1>Hello Spring-Security !!!</h1> </body> </html>
啟動項目訪問項目地址:
http://locahost:8080
會發現被重定向到這個地址,並且要求登陸賬號
http://localhost:8080/login

Spring-Security默認的用戶是user,但是密碼是由組件Spring-Security-Test隨機生成的
在控制台輸出可以找到:

登陸成功之后,才會跳轉到頁面中來:

參考地址:
https://www.bilibili.com/video/BV12D4y1U7D8?p=3
三、UserDetailService接口分析
package org.springframework.security.core.userdetails;
public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
只有一個接口方法,通過用戶名加載賬號信息
注意,返回的是一個UserDetails類型對象,賬號細節實例
再來看UserDetails接口:
package org.springframework.security.core.userdetails;
import java.io.Serializable;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
該類型也是一個接口,不過多出了其他一些方法:
1、Collection<? extends GrantedAuthority> getAuthorities(); 獲取賬戶對應的所有權限的集合對象 2、String getPassword(); 獲取密碼 3、String getUsername(); 獲取用戶名 4、boolean isAccountNonExpired(); 賬戶是否過期,過期的賬戶不可以被認證 5、boolean isAccountNonLocked(); 賬戶狀態是否為鎖定,鎖定的賬戶不可以被認證 6、boolean isCredentialsNonExpired(); 憑證是否過期,憑證即密碼 7、boolean isEnabled(); 賬戶狀態是否為可用
相比於Shiro來說,UserDetailsService更像Shiro的Realm
而這個UserDetails對象在ShIro中是需要我們自己來實現的,例如之前的項目中的ActivateUser。
注意上面權限集合類型,官方文檔特別注釋該方法返回不可以為NULL。
當然,Security也提供了對應的一些實現類:

四、密碼加密 PasswordEncoder接口
package org.springframework.security.crypto.password;
public interface PasswordEncoder {
String encode(CharSequence var1);
boolean matches(CharSequence var1, String var2);
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
API說明:
1、String encode(CharSequence var1); 設置密碼加密的方法,加密邏輯自行實現 2、matches(CharSequence var1, String var2); 匹配判斷,var1 是原始密碼,var2 是加密的密碼 匹配邏輯自行實現 3、upgradeEncoding(String encodedPassword) { 判斷是否能夠二次加密密碼,一般不使用,且默認false
該接口下的實現類:

官方推薦的實現類使用這個:
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
API測試:
package cn.zeal4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@SpringBootTest
class SecurityApplicationTests {
@Test
void contextLoads() {
cryptTest();
}
private static void cryptTest() {
final String PASSWORD = "123456";
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encode = passwordEncoder.encode(PASSWORD);
System.out.println(encode);
boolean matches = passwordEncoder.matches(PASSWORD, encode);
System.out.println(matches);
}
}
結果:
$2a$10$NNySuHkEHtzLHodCivAFN.FvakFpR6/tSpkgzDW4QPd8PMF5IBaza true
