spring security 超詳細使用教程及如何接入springboot、前后端分離
Spring Security 是一個強大且可擴展的框架,用于保護 Java 應用程序,尤其是基于 Spring 的應用。它提供了身份驗證(驗證用戶身份)、授權(管理用戶權限)和防護機制(如 CSRF 保護和防止會話劫持)等功能。
Spring Security 允許開發(fā)者通過靈活的配置實現(xiàn)安全控制,確保應用程序的數(shù)據(jù)和資源安全。通過與其他 Spring 生態(tài)系統(tǒng)的無縫集成,Spring Security 成為構建安全應用的理想選擇。
核心概念
- 身份驗證 (Authentication): 驗證用戶的身份(例如,用戶名/密碼)。
- 授權 (Authorization): 確定用戶是否有權限訪問特定資源。
- 安全上下文 (Security Context): 存儲已認證用戶的詳細信息,應用程序中可以訪問。
1、準備工作
1.1 引入依賴
當我們引入 security 依賴后,訪問需要授權的 url 時,會重定向到 login 頁面(security 自己創(chuàng)建的),login 頁面需要賬號密碼,賬號默認是 user, 密碼是隨機的字符串,在spring項目的輸出信息中
spring-boot-starter-security

<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>jjwt-api

<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>jjwt-impl

<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>jjwt-jackson

<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>一般我們會創(chuàng)建一個 SecurityConfig 類,來管理我們所有與 security 相關的配置。(我們講的是 security 5.7 版本之后的配置方法,之前的方法跟現(xiàn)在不太一樣)
@Configuration
@EnableWebSecurity // 該注解啟用 Spring Security 的 web 安全功能。
public class SecurityConfig {
}下面的都要寫到 SecurityConfig 類中
1.2 用戶認證的配置
基于內(nèi)存的用戶認證
通過 createUser , manager 把用戶配置的賬號密碼添加到spring的內(nèi)存中, InMemoryUserDetailsManager 類中有一個 loadUserByUsername 的方法通過賬號(username)從內(nèi)存中獲取我們配置的賬號密碼,之后調(diào)用其他方法來判斷前端用戶輸入的密碼和內(nèi)存中的密碼是否匹配。
@Bean
public UserDetailsService userDetailsService() {
// 創(chuàng)建基于內(nèi)存的用戶信息管理器
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(
// 創(chuàng)建UserDetails對象,用于管理用戶名、用戶密碼、用戶角色、用戶權限等內(nèi)容
User.withDefaultPasswordEncoder().username("user").password("user123").roles("USER").build()
);
// 如果自己配置的有賬號密碼, 那么上面講的 user 和 隨機字符串 的默認密碼就不能用了
return manager;
}當我們點進 InMemoryUserDetailsManager 中 可以發(fā)現(xiàn)它實現(xiàn)了 UserDetailsManager 和 UserDetailsPasswordService 接口,其中 UserDetailsManager 接口繼承的 UserDetailsService 接口中就有 loadUserByUsername 方法



基于數(shù)據(jù)庫的用戶認證
上面講到,spring security 是通過 loadUserByUsername 方法來獲取 User 并用這個 User 來判斷用戶輸入的密碼是否正確。所以我們只需要繼承 UserDetailsService 接口并重寫 loadUserByUsername 方法即可
下面的樣例我用的 mybatis-plus 來查詢數(shù)據(jù)庫中的 user, 然后通過當前查詢到的 user 返回特定的 UserDetails 對象
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
queryWrapper.eq("username", username); // 這里不止可以用username,你可以自定義,主要根據(jù)你自己寫的查詢邏輯
User user = userMapper.selectOne(queryWrapper);
if (user == null) {
throw new UsernameNotFoundException(username);
}
return new UserDetailsImpl(user); // UserDetailsImpl 是我們實現(xiàn)的類
}
}UserDetailsImpl 是實現(xiàn)了 UserDetails 接口的類。UserDetails 接口是 Spring Security 身份驗證機制的基礎,通過實現(xiàn)該接口,開發(fā)者可以定義自己的用戶模型,并提供用戶相關的信息,以便進行身份驗證和權限檢查。
@Data
@AllArgsConstructor
@NoArgsConstructor // 這三個注解可以幫我們自動生成 get、set、有參、無參構造函數(shù)
public class UserDetailsImpl implements UserDetails {
private User user; // 通過有參構造函數(shù)填充賦值的
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of();
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() { // 檢查賬戶是否 沒過期。
return true;
}
@Override
public boolean isAccountNonLocked() { // 檢查賬戶是否 沒有被鎖定。
return true;
}
@Override
public boolean isCredentialsNonExpired() { //檢查憑據(jù)(密碼)是否 沒過期。
return true;
}
@Override
public boolean isEnabled() { // 檢查賬戶是否啟用。
return true;
}
// 這個方法是 @Data注解 會自動幫我們生成,用來獲取 loadUserByUsername 中最后我們返回的創(chuàng)建UserDetailsImpl對象時傳入的User。
// 如果你的字段包含 username和password 的話可以用強制類型轉(zhuǎn)換, 把 UserDetailsImpl 轉(zhuǎn)換成 User。如果不能強制類型轉(zhuǎn)換的話就需要用到這個方法了
public User getUser() {
return user;
}
}1.3 基本的配置
下面這個是 security 的默認配置。我們可以修改并把它加到spring容器中,完成我們特定的需求。
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 開啟授權保護
.authorizeHttpRequests(authorize -> authorize
// 不需要認證的地址有哪些
.requestMatchers("/blog/**", "/public/**", "/about").permitAll() // ** 通配符
// 對所有請求開啟授權保護
.anyRequest().
// 已認證的請求會被自動授權
authenticated()
)
// 使用默認的登陸登出頁面進行授權登陸
.formLogin(Customizer.withDefaults())
// 啟用“記住我”功能的。允許用戶在關閉瀏覽器后,仍然保持登錄狀態(tài),直到他們主動注銷或超出設定的過期時間。
.rememberMe(Customizer.withDefaults());
// 關閉 csrf CSRF(跨站請求偽造)是一種網(wǎng)絡攻擊,攻擊者通過欺騙已登錄用戶,誘使他們在不知情的情況下向受信任的網(wǎng)站發(fā)送請求。
http.csrf(csrf -> csrf.disable());
return http.build();
}關于上面放行路徑寫法的一些細節(jié)問題:
如果在 application.properities 中配置的有 server.servlet.context-path=/api 前綴的話,在放行路徑中不需要寫 /api。
如果 @RequestMapping(value = "/test/") 中寫的是 /test/, 那么放行路徑必須也寫成 /test/, (/test)是不行的,反之亦然。
如果 @RequestMapping(value = "/test") 鏈接 /test 后面要加查詢字符的話(/test?type=0),不要寫成 @RequestMapping(value = "/test/")
上面都是一些細節(jié)問題,但是遇到 bug 的時候不容易發(fā)現(xiàn)。(筆者初學時找了一個小時,… … .)
1.4 常用配置
下面這個是我常用的配置,配置了jwt,加密的類,過濾器啟用 jwt,而不是session。(jwt的類會在下面講到)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(CsrfConfigurer::disable) // 基于token,不需要csrf
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 基于token,不需要session
.authorizeHttpRequests((authz) -> authz
.requestMatchers("/login/", "/getPicCheckCode").permitAll() // 放行api
.requestMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}2、加密
Spring Security 提供了多種加密和安全機制來保護用戶的敏感信息,尤其是在用戶身份驗證和密碼管理方面。(我們只講默認的 BCryptPasswordEncoder)
1. 密碼加密的重要性
在應用程序中,用戶密碼是最敏感的數(shù)據(jù)之一。為了防止密碼泄露,即使數(shù)據(jù)庫被攻擊者獲取,密碼也應以加密形式存儲。加密可以保護用戶的隱私,并在一定程度上增加安全性。
2. Spring Security 的加密機制
2.1 PasswordEncoder 接口
Spring Security 提供了 PasswordEncoder 接口,用于定義密碼的加密和驗證方法。主要有以下幾種實現(xiàn):
- BCryptPasswordEncoder:基于 BCrypt 算法,具有適應性和強大的加密強度。它可以根據(jù)需求自動調(diào)整加密的復雜性。
- NoOpPasswordEncoder:不執(zhí)行加密,適用于開發(fā)和測試環(huán)境,不建議在生產(chǎn)環(huán)境中使用。
- Pbkdf2PasswordEncoder、Argon2PasswordEncoder 等:這些都是基于不同算法的實現(xiàn),具有不同的安全特性。
2.2 BCrypt 算法
BCrypt 是一種安全的密碼哈希算法,具有以下特點:
- 鹽值(Salt):每次加密時都會生成一個隨機鹽值,確保相同的密碼在每次加密時生成不同的哈希值。
- 適應性:通過增加計算復雜性(如工作因子),可以提高密碼的加密強度。
3. 使用 PasswordEncoder
3.1 配置 PasswordEncoder
可以在 Spring Security 的配置類中定義 PasswordEncoder bean,例如:
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
// 也可用有參構造,取值范圍是 4 到 31,默認值為 10。數(shù)值越大,加密計算越復雜
return new BCryptPasswordEncoder();
}
}3.2 加密密碼
在注冊用戶或更新密碼時,可以使用 PasswordEncoder 來加密密碼:
@Autowired
private PasswordEncoder passwordEncoder;
public void registerUser(String username, String rawPassword) {
String encryptedPassword = passwordEncoder.encode(rawPassword);
// 保存用戶信息到數(shù)據(jù)庫,包括加密后的密碼
}3.3 驗證密碼
在用戶登錄時,可以使用 matches 方法驗證輸入的密碼與存儲的加密密碼是否匹配:
public boolean login(String username, String rawPassword) {
// 從數(shù)據(jù)庫中獲取用戶信息,包括加密后的密碼
String storedEncryptedPassword = // 從數(shù)據(jù)庫獲取;
return passwordEncoder.matches(rawPassword, storedEncryptedPassword);
}3、前后端分離
1. 用戶認證流程
1.1 用戶登錄
前端:
- 用戶在登錄界面輸入用戶名和密碼。
- 前端將這些憑證以 JSON 格式發(fā)送到后端的登錄 API(例如
POST /api/login)。
后端:
- Spring Security 接收請求,使用
AuthenticationManager進行身份驗證。 - 如果認證成功,后端生成一個 JWT(JSON Web Token)或其他認證令牌,并將其返回給前端。
2. 使用 JWT 進行用戶認證
2.1 前端存儲 JWT
- 前端收到 JWT 后,可以將其存儲在
localStorage或sessionStorage中,以便后續(xù)請求使用。
2.2 發(fā)送受保護請求
在發(fā)送需要認證的請求時,前端將 JWT 添加到請求頭中:
fetch('/api/protected-endpoint', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
});2.3 后端解析
JWT Spring Security 通過過濾器來解析和驗證 JWT??梢宰远x一個 OncePerRequestFilter 以攔截請求,提取 JWT,并驗證其有效性。
3. 退出登錄
由于 JWT 是無狀態(tài)的,后端不需要記錄會話狀態(tài)。用戶可以通過前端操作(例如,刪除存儲的 JWT)來“退出登錄”。可以實現(xiàn)一個注銷接口,用于前端執(zhí)行相關邏輯。
4. 保護敏感信息
- 確保 HTTPS:在前后端通信中使用 HTTPS,確保傳輸中的數(shù)據(jù)安全。
- 令牌過期:設置 JWT 的有效期,過期后需要用戶重新登錄。
- 刷新令牌:可以實現(xiàn)刷新令牌的機制,以提高用戶體驗。
4. 實現(xiàn) jwt
4.1 OncePerRequestFilter
- 作用:
OncePerRequestFilter是 Spring Security 提供的一個抽象類,確保在每個請求中只執(zhí)行一次特定的過濾邏輯。它是實現(xiàn)自定義過濾器的基礎,通常用于對請求進行預處理或后處理。(實現(xiàn)JWT會用到這個接口) - 功能:提供了一種機制,以確保過濾器的邏輯在每個請求中只執(zhí)行一次,非常適合需要對每個請求進行處理的場景。通過繼承該類,可以輕松實現(xiàn)自定義過濾器適合用于記錄日志、身份驗證、權限檢查等場景。
- 實現(xiàn):繼承
OncePerRequestFilter類,并重寫doFilterInternal方法。
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// 自定義過濾邏輯,例如記錄請求日志
System.out.println("Request URI: " + request.getRequestURI());
// 繼續(xù)執(zhí)行過濾鏈
filterChain.doFilter(request, response);
}
}注冊過濾器:可以在 Spring Security 配置類中注冊自定義的過濾器。
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.addFilterBefore(new CustomFilter(), UsernamePasswordAuthenticationFilter.class);
// 其他配置...
return http.build();
}
}4.2 生成 JWT
基于我們上面引入的三個 JWT 相關的依賴,寫JwtUtil 類
- 功能:
JwtUtil類提供了生成和解析 JWT 的功能,是實現(xiàn)基于 JWT 的認證和授權的重要工具。 - 應用場景:可以在用戶登錄后生成 JWT,并在后續(xù)請求中攜帶 JWT 用于用戶身份驗證和權限校驗。通過設置合適的過期時間,可以提高安全性。
@Component
public class JwtUtil {
public static final long JWT_TTL = 60 * 60 * 1000L * 24 * 14; // 有效期14天
public static final String JWT_KEY = "SDFGjhdsfalshdfHFdsjkdsfds121232131afasdfac";
public static String getUUID() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
public static String createJWT(String subject) {
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
return builder.compact();
}
private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
SecretKey secretKey = generalKey();
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
if (ttlMillis == null) {
ttlMillis = JwtUtil.JWT_TTL;
}
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
return Jwts.builder()
.id(uuid)
.subject(subject)
.issuer("sg")
.issuedAt(now)
.signWith(secretKey)
.expiration(expDate);
}
public static SecretKey generalKey() {
byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");
}
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parser()
.verifyWith(secretKey)
.build()
.parseSignedClaims(jwt)
.getPayload();
}
}1. 類注解與常量
@Component:將這個類標記為 Spring 組件,允許 Spring 管理該類的生命周期,便于依賴注入。JWT_TTL:JWT 的有效期,設置為 14 天(單位為毫秒)。JWT_KEY:用于簽名的密鑰字符串,經(jīng)過 Base64 編碼。它是生成和驗證 JWT 的關鍵。
2. UUID 生成
public static String getUUID() {
return UUID.randomUUID().toString().replaceAll("-", "");
}- 作用:生成一個唯一的 UUID,去掉了其中的連字符。這通常用作 JWT 的 ID(
jti),確保每個 JWT 都是唯一的。
3. 創(chuàng)建 JWT
public static String createJWT(String subject) {
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
return builder.compact();
}- 參數(shù):
subject是 JWT 的主題,通常是用戶的身份信息。 - 功能:調(diào)用
getJwtBuilder方法生成一個 JWT 構建器,并將其壓縮為字符串返回。
4. JWT 構建器
private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
//...
}功能:生成一個 JWT 的構建器,配置 JWT 的各個屬性,包括:
setId(uuid):設置 JWT 的唯一標識。setSubject(subject):設置 JWT 的主題。setIssuer("sg"):設置 JWT 的發(fā)行者,通常是你的應用或服務名稱。setIssuedAt(now):設置 JWT 的簽發(fā)時間。signWith(signatureAlgorithm, secretKey):使用指定的算法和密鑰進行簽名。setExpiration(expDate):設置 JWT 的過期時間。
5. 生成密鑰
public static SecretKey generalKey() {
byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");
}功能:將 JWT_KEY 進行 Base64 解碼,生成一個 SecretKey 對象,用于 JWT 的簽名和驗證。使用 HMAC SHA-256 算法進行簽名。
6. 解析 JWT
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(jwt)
.getBody();
}- 參數(shù):
jwt是要解析的 JWT 字符串。 - 功能: 使用之前生成的密鑰解析 JWT。
- 使用之前生成的密鑰解析 JWT。
- 如果 JWT 有效,將返回 JWT 的聲明(
Claims對象),其中包含了 JWT 的主題、發(fā)行者、過期時間等信息。 - 該方法可能會拋出異常,表示 JWT 無效或解析失敗。
4.3 應用 JWT
先加一個依賴 JetBrains Java Annotations, 下面的代碼會用到其中的一個注解 @NotNull。

這個 JwtAuthenticationTokenFilter 類是一個實現(xiàn)了 OncePerRequestFilter 接口自定義的 Spring Security 過濾器。
- 功能:
JwtAuthenticationTokenFilter通過 JWT 對用戶進行身份驗證,確保請求中攜帶的 JWT 是有效的,并根據(jù) JWT 提供的用戶信息設置認證狀態(tài)。 - 應用場景:通常用于保護需要身份驗證的 API 接口,確保只有經(jīng)過認證的用戶才能訪問資源。該過濾器通常放置在 Spring Security 過濾鏈的合適位置,以確保在處理請求之前進行身份驗證。
import io.jsonwebtoken.Claims;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private UserMapper userMapper;
@Override
protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
token = token.substring(7);
String userid;
try {
Claims claims = JwtUtil.parseJWT(token);
userid = claims.getSubject();
} catch (Exception e) {
throw new RuntimeException(e);
}
User user = userMapper.selectById(Integer.parseInt(userid));
if (user == null) {
throw new RuntimeException("用戶名未登錄");
}
UserDetailsImpl loginUser = new UserDetailsImpl(user);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser, null, null);
// 如果是有效的jwt,那么設置該用戶為認證后的用戶
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
}
}1. 類注解與繼承
@Component:標記該類為 Spring 組件,使其能夠被 Spring 掃描并注冊到應用上下文中。- 繼承
OncePerRequestFilter:確保每個請求只調(diào)用一次該過濾器,適合進行請求預處理。
2. 依賴注入
@Autowired private UserMapper userMapper;
UserMapper:一個數(shù)據(jù)訪問對象(DAO),用于與數(shù)據(jù)庫交互,以便根據(jù)用戶 ID 查詢用戶信息。通過 Spring 的依賴注入機制自動注入。
3 獲取 JWT
String token = request.getHeader("Authorization");
if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}- 從請求頭中獲取
Authorization字段的值,檢查是否包含 JWT。 - 如果沒有 JWT 或格式不正確,直接調(diào)用
filterChain.doFilter(request, response)繼續(xù)執(zhí)行下一個過濾器,返回。
4 解析 JWT
token = token.substring(7); // 去掉 "Bearer " 前綴
String userid;
try {
Claims claims = JwtUtil.parseJWT(token);
userid = claims.getSubject();
} catch (Exception e) {
throw new RuntimeException(e);
}- 從 token 中去掉 "Bearer " 前綴,只保留 JWT 部分。
- 調(diào)用
JwtUtil.parseJWT(token)解析 JWT,提取出用戶 ID (userid)。如果解析失敗,會拋出異常。
5 查詢用戶信息
User user = userMapper.selectById(Integer.parseInt(userid));
if (user == null) {
throw new RuntimeException("用戶名未登錄");
}根據(jù)解析出的用戶 ID 從數(shù)據(jù)庫中查詢用戶信息。如果用戶不存在,拋出異常。
6 設置安全上下文
UserDetailsImpl loginUser = new UserDetailsImpl(user);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser, null, null);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);- 創(chuàng)建一個自定義的
UserDetailsImpl對象,將查詢到的用戶信息封裝。 - 創(chuàng)建一個
UsernamePasswordAuthenticationToken對象,表示用戶的認證信息,并將其設置到 Spring Security 的SecurityContextHolder中,以便后續(xù)請求能夠訪問到用戶的認證信息。
4. 繼續(xù)過濾鏈
filterChain.doFilter(request, response);
調(diào)用 filterChain.doFilter(request, response),繼續(xù)執(zhí)行后續(xù)的過濾器和最終的請求處理。
4.4 注冊自定義的 JwtAuthenticationTokenFilter 過濾器
把我們的過濾器應用到 spring security 中
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
5、常用操作
5.1 配置 AuthenticationManager
將 AuthenticationManager 對象添加到spring容器中.(添加到我們創(chuàng)建的 SecurityConfig 配置類中)
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}5.2 驗證當前用戶
@Autowired private AuthenticationManager authenticationManager; UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); // 從數(shù)據(jù)庫中對比查找,如果找到了會返回一個帶有認證的封裝后的用戶,否則會報錯,自動處理。(這里我們假設我們配置的security是基于數(shù)據(jù)庫查找的) Authentication authenticate = authenticationManager.authenticate(authenticationToken); // 獲取認證后的用戶 User user = (User ) authenticate.getPrincipal(); // 如果強制類型轉(zhuǎn)換報錯的話,可以用我們實現(xiàn)的 `UserDetailsImpl` 類中的 getUser 方法了 String jwt = JwtUtil.createJWT(user.getId().toString());
5.3 獲取當前用戶的認證信息
以下是獲取當前用戶認證信息的具體步驟:
// SecurityContextHolder 是一個存儲安全上下文的工具類,提供了一個全局訪問點,用于獲取當前請求的安全上下文。 // SecurityContext 是當前線程的安全上下文,包含了當前用戶的認證信息(即 Authentication 對象)。 SecurityContext context = SecurityContextHolder.getContext(); Authentication authentication = context.getAuthentication(); User user = (User ) authenticate.getPrincipal(); // 另一種獲取 User 的方法 UsernamePasswordAuthenticationToken authenticationToken = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); UserDetailsImpl loginUser = (UserDetailsImpl) authenticationToken.getPrincipal(); User user = userDetails.getUser();
5.4 對比 authenticationManager 和 SecurityContext 獲取 Authentication
SecurityContextHolder.getContext().getAuthentication():
- 這個方法獲取當前線程的安全上下文中的
Authentication對象。 - 通常在用戶已經(jīng)被認證后使用,用于獲取當前用戶的信息(如身份、權限等)。
authenticationManager.authenticate(authenticationToken):
- 這個方法是用于實際執(zhí)行身份驗證的過程。
- 它接收一個憑證(如用戶名和密碼)并返回一個經(jīng)過驗證的
Authentication對象,或者拋出異常。 - 在用戶登錄或驗證時使用,屬于身份驗證的實際邏輯。
5.5 Authentication 接口
Authentication 接口包含以下重要方法:
getPrincipal():返回當前用戶的身份,通常是用戶的詳細信息(如用戶名)。getCredentials():返回用戶的憑證(如密碼),但通常不直接使用此方法。getAuthorities():返回用戶的權限列表(角色或權限)。
6、授權
Spring Security 的授權機制用于控制用戶對應用程序資源的訪問權限。授權通常是基于用戶角色或權限的,以下是對 Spring Security 授權的詳細講解。
1. 授權的基本概念
授權(Authorization):指的是決定用戶是否有權限訪問特定資源的過程。在用戶認證成功后,系統(tǒng)會根據(jù)預定義的規(guī)則來判斷用戶是否可以訪問某些資源。
2. 授權的主要組成部分
2.1 權限與角色
- 權限(Permission):通常指對特定資源的操作能力,如“讀取”、“寫入”或“刪除”。
- 角色(Role):一組權限的集合。例如,管理員角色可能具有所有權限,而普通用戶角色可能只有讀取權限。
2.2 GrantedAuthority 接口
- Spring Security 使用
GrantedAuthority接口表示用戶的權限。每個用戶的權限可以通過實現(xiàn)該接口的對象來表示。
3. 授權方式
Spring Security 支持多種授權方式,主要包括:
3.1 基于角色的授權
- 定義角色:通常在用戶注冊或用戶管理中定義。
- 配置角色授權:可以在 Spring Security 配置類中設置基于角色的訪問控制。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN") // 只有 ADMIN 角色可以訪問
.antMatchers("/user/**").hasAnyRole("USER", "ADMIN") // USER 和 ADMIN 角色可以訪問
.anyRequest().authenticated(); // 其他請求需要認證
}3.2 基于權限的授權
- 定義權限:與角色類似,定義更細粒度的權限。
- 配置權限授權:在配置類中設置基于權限的訪問控制。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/edit/**").hasAuthority("EDIT_PRIVILEGE") // 僅具有 EDIT_PRIVILEGE 權限的用戶可以訪問
.anyRequest().authenticated();
}4. 自定義授權邏輯
如果需要更復雜的授權邏輯,可以實現(xiàn)自定義的 AccessDecisionVoter 或 AccessDecisionManager。
4.1 AccessDecisionVoter 負責評估用戶的訪問請求,返回授權決策。
4.2 AccessDecisionManager
管理多個 AccessDecisionVoter 的調(diào)用,確定用戶是否有權訪問請求的資源。
7、其他自定義行為
以下接口和類用于處理不同的安全事件,提供了自定義處理的能力:
1. AuthenticationSuccessHandler
- 作用:用于處理用戶成功認證后的邏輯。
- 功能:可以自定義成功登錄后的跳轉(zhuǎn)行為,比如重定向到特定頁面、返回 JSON 響應等。
- 實現(xiàn):實現(xiàn)
AuthenticationSuccessHandler接口,并重寫onAuthenticationSuccess方法。
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
}
}
// 讓我們實現(xiàn)的類生效
http.formLogin(form ->
form.successHandler(new MyAuthenticationSuccessHandler()));2. AuthenticationFailureHandler
- 作用:用于處理用戶認證失敗的邏輯。
- 功能:可以自定義失敗登錄后的行為,比如返回錯誤信息、重定向到登錄頁面并顯示錯誤提示等。
- 實現(xiàn):實現(xiàn)
AuthenticationFailureHandler接口,并重寫onAuthenticationFailure方法。
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
}
}
// 讓我們實現(xiàn)的類生效
http.formLogin(form ->
form.failureHandler(new MyAuthenticationFailureHandler()));3. LogoutSuccessHandler
- 作用:用于處理用戶成功登出的邏輯。
- 功能:可以自定義注銷成功后的行為,比如重定向到登錄頁面、顯示注銷成功的消息等。
- 實現(xiàn):實現(xiàn)
LogoutSuccessHandler接口,并重寫onLogoutSuccess方法。
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
}
}
// 讓我們實現(xiàn)的類生效
http.logout(logout -> {
logout.logoutSuccessHandler(new MyLogoutSuccessHandler());
});4. AuthenticationEntryPoint
- 作用:用于處理未認證用戶訪問受保護資源時的邏輯。
- 功能:可以自定義未認證用戶的響應,比如返回 401 狀態(tài)碼、重定向到登錄頁面等。
- 實現(xiàn):實現(xiàn)
AuthenticationEntryPoint接口,并重寫commence方法。
// 重寫 AuthenticationEntryPoint 接口
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
}
}
// 讓我們重寫的類生效
http.exceptionHandling(exception -> {
exception.authenticationEntryPoint(new MyAuthenticationEntryPoint());
});5. SessionInformationExpiredStrategy
- 作用:用于處理用戶會話過期的邏輯。
- 功能:可以自定義會話超時后的響應,比如重定向到登錄頁面或返回 JSON 響應等。
- 實現(xiàn):實現(xiàn)
SessionInformationExpiredStrategy接口,并重寫onExpiredSession方法。
// 重寫 SessionInformationExpiredStrategy 接口
public class MySessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
@Override
public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {
}
}
// 讓我們重寫的類生效
http.sessionManagement(session -> {
session.maximumSessions(1).expiredSessionStrategy(new MySessionInformationExpiredStrategy());
});6. AccessDeniedHandler
- 作用:用于處理用戶訪問被拒絕的情況。當用戶嘗試訪問沒有權限的資源時,Spring Security 會調(diào)用實現(xiàn)了
AccessDeniedHandler接口的處理器。 - 功能:可以自定義拒絕訪問后的響應行為,比如重定向到特定的錯誤頁面、返回錯誤信息或 JSON 響應。
- 實現(xiàn):實現(xiàn)
AccessDeniedHandler接口,并重寫handle方法。
// 重寫 AccessDeniedHandler 接口
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
}
}
// 在安全配置中注冊自定義的 AccessDeniedHandler
http.exceptionHandling(exception -> {
exception.accessDeniedHandler(new MyAccessDeniedHandler());
});
});文章到這里就結束了~
到此這篇關于spring security 超詳細使用教程(接入springboot、前后端分離)的文章就介紹到這了,更多相關spring security 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- SpringBoot3.x接入Security6.x實現(xiàn)JWT認證的完整步驟
- SpringBoot整合Springsecurity實現(xiàn)數(shù)據(jù)庫登錄及權限控制功能
- springboot版本升級以及解決springsecurity漏洞的問題
- SpringBoot的Security和OAuth2的使用示例小結
- SpringBoot集成SpringSecurity安全框架方式
- SpringBoot+SpringSecurity實現(xiàn)認證的流程詳解
- SpringSecurity角色權限控制(SpringBoot+SpringSecurity+JWT)
- SpringBoot整合SpringSecurity和JWT和Redis實現(xiàn)統(tǒng)一鑒權認證
相關文章
MyBatis利用攔截器實現(xiàn)數(shù)據(jù)脫敏詳解
現(xiàn)代網(wǎng)絡環(huán)境中,敏感數(shù)據(jù)的處理是至關重要的,敏感數(shù)據(jù)包括個人身份信息、銀行賬號、手機號碼等,所以本文主要為大家詳細介紹了MyBatis如何利用攔截器實現(xiàn)數(shù)據(jù)脫敏,希望對大家有所幫助2023-11-11
Java異步編程之Callbacks與Futures模型詳解
這篇文章主要為大家詳細介紹了Java異步編程中Callbacks與Futures模型的使用,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2023-03-03
springboot如何接收復雜參數(shù)(同時接收JSON與文件)
文章介紹了在Spring Boot中同時處理JSON和文件上傳時使用`@RequestPart`注解的方法,`@RequestPart`可以接收多種格式的參數(shù),包括JSON和文件,并且可以作為`multipart/form-data`格式中的key2025-02-02

