Spring Security 是什麼?
-
Spring Security 是 Spring 生態系統中用於安全保護的核心框架
-
傳統Servlet架構會將
Authentication
物件存在Session(前端是Cookie),前後端分離架構則是存在Token(前端是LocalStorage).
解決了什麼問題?
- 認證 Authentication
- 認證確保使用者是合法身份,例如透過帳號密碼、OAuth2、JWT 或 LDAP 進行登入
- 授權 Authorization
- 授權控制確立使用者可以訪問哪些資源,防止未授權的人操作敏感功能或資料。Spring Security 允許基於角色或權限進行細粒度存取控制 。
核心組件有哪些?
1. SecurityContextHolder 和 SecurityContext
-
SecurityContextHolder:保存應用當前使用者的安全上下文資訊,預設使用
ThreadLocal
讓每個執行緒都有自己的安全資料 。 -
SecurityContext:實際儲存當前使用者的認證資訊(即
Authentication
對象),供整個應用存取。
//通常在JwtAuthenticationFilter中
//將認證資訊存入SecurityContext
SecurityContextHolder.getContext().setAuthentication(authToken);
2. Authentication 與 GrantedAuthority
- Authentication:代表使用者的登入狀態,包含使用者身份(principal)、憑證(credentials)和授權資訊(authorities) 。
//通常出現在兩個地方
//1.login方法
//建立一個已驗證的通行證
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(newLogin.getUsername(), newLogin.getPassword()));
//2.JwtAuthenticationFilter
//UsernamePasswordAuthenticationToken 物件是 Authentication 的一個標準實作。
// 這張 "通行證" 包含了三樣東西:
// - Principal (你是誰): username 字串
// - Credentials (憑證): null (因為 JWT 已經驗證過了,不需要密碼)
// - Authorities (你能做什麼): 上一步建立的 authorities 列表
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
username,
null,
authorities
);
//將認證資訊存入 SecurityContext
SecurityContextHolder.getContext().setAuthentication(authToken);
- GrantedAuthority:定義使用者所擁有的權限(如
ROLE_USER
或ROLE_ADMIN
),用於授權判斷。
//Token通過認證後直接從Token中提取權限,不訪問資料庫
List<String> roles = jwtService.extractRoles(jwt);
//這裡 "建立" 了 GrantedAuthority 物件
List<SimpleGrantedAuthority> authorities = roles.stream()
.mapnew
.collect(Collectors.toList());
//之後傳入了上方的UsernamePasswordAuthenticationToken
3. UserDetails 與 UserDetailsService
-
UserDetails:定義使用者帳號、密碼、是否啟用、是否被鎖定等屬性 。
-
UserDetailsService:透過使用者名稱載入
UserDetails
資料(通常從資料庫讀取),是認證流程的核心介面 。- 是一個介面,要自己實作
loadUserByUsername(String username)
方法
- 是一個介面,要自己實作
//在JpaUserDetailsService中,實作了UserDetailsService中的方法
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
User user = userRepository.findByUsername(username)
.orElseThrow(()->new UsernameNotFoundException("找不到使用者" + "username"));
return new SecurityUser(user);
}
4. AuthenticationManager 與 AuthenticationProvider
-
AuthenticationManager:Spring Security 認證流程的核心入口,定義了
authenticate()
方法,用於統一處理身份驗證 。 -
AuthenticationProvider:實際執行認證邏輯的實作類,可有多個 provider 被同一個 manager 管理,支援多種登入模式(如表單登入、JWT、LDAP 等)。
//通常在login方法中
//在這裡authenticationManager在得到UsernamePasswordAuthenticationToken後在內部呼叫了
//authenticationProvider來處理,並返回了經過驗證的authentication
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(newLogin.getUsername(), newLogin.getPassword()));
5. AccessDecisionManager 與 AbstractSecurityInterceptor
-
AccessDecisionManager:決定使用者是否有權限訪問指定資源。它會使用一組
AccessDecisionVoter
(投票器)根據角色或權限進行表決 。 -
AbstractSecurityInterceptor:為安全攔截器的基類,例如
FilterSecurityInterceptor
(Web 層)與MethodSecurityInterceptor
(方法層)均繼承自此類。 -
幾乎永遠不會「直接」 在你的程式碼中呼叫或實作它們
6. 過濾器鏈(Filter Chain)
Spring Security 透過一系列過濾器(Filters)來攔截請求並執行安全檢查,可以把「過濾器鏈 (Filter Chain)」想像成一個機場的安檢流程,例如:
-
SecurityContextHolderFilter
(安全上下文持有者過濾器)-
工作: 在請求開始時,建立一個空的
SecurityContextHolder
(保管箱);並在請求結束時,負責清除它。這是確保「執行緒隔離」和「請求結束後不殘留登入資訊」的核心。 -
標注: (自動配置) - Spring Security 6 之後的標準 Filter,你不需要手動配置它。
-
-
CorsFilter
(跨域資源共用過濾器)-
工作: 檢查請求是否符合你設定的 CORS 規則(例如,是否來自你允許的前端網域
http://localhost:3000
)。如果是非法網域,它會直接拒絕。 -
標注: (需添加 Config) - 你必須在
SecurityConfig
中提供一個CorsConfigurationSource
Bean (或使用http.cors(...)
) 來定義跨域規則。
-
-
LogoutFilter
(登出過濾器)-
工作: 監聽預設的
/logout
路徑。如果請求是/logout
,它會執行登出邏輯(例如清除SecurityContextHolder
、使 Session 失效等)。 -
標注: (需添加 Config) - 你通常會使用
http.logout(...)
來客製化登出路徑或登出成功後的處理方式。
-
-
JwtAuthenticationFilter
(JWT 認證過濾器)-
工作: 這是你的核心! 它攔截所有請求,檢查
Authorization
Header,驗證 JWT Token,如果成功,就解析出用戶資訊和權限 (GrantedAuthority
),並建立Authentication
物件存入SecurityContextHolder
。 -
標注: (需自己實作) -
JwtAuthenticationFilter.java
檔案。
-
-
AnonymousAuthenticationFilter
(匿名認證過濾器)-
工作: 在你的
JwtAuthenticationFilter
執行之後,它會檢查SecurityContextHolder
是否仍然是空的(例如:用戶根本沒帶 Token,或是訪問公開 API)。如果是空的,它會放入一個代表「匿名用戶」的Authentication
物件(權限為ROLE_ANONYMOUS
)。 -
標注: (自動配置) - 這是內建的,確保
SecurityContextHolder
永遠不會是null
,這有助於AuthorizationFilter
處理permitAll()
規則。
-
-
SessionManagementFilter
(Session 管理過濾器)-
工作: 極度重要! 它會根據你的配置(
sessionCreationPolicy(STATELESS)
) 來強制執行「無狀態」策略,確保 Spring Security 絕對不會建立或使用 HTTP Session 來儲存安全資訊。 -
標注: (需添加 Config) - 你必須在
SecurityConfig
中設定STATELESS
,這是 JWT 運作的基礎。
-
-
ExceptionTranslationFilter
(例外轉譯過濾器)-
工作: 極度重要! 這是「安全錯誤處理中心」。它本身不執行安全邏輯,但它會
try-catch
後面 Filter 拋出的安全例外:-
如果它抓到
AuthenticationException
(認證失敗,例如 Token 無效),它會回傳 401 Unauthorized。 -
如果它抓到
AccessDeniedException
(授權失敗,例如ROLE_USER
想訪問ADMIN
資源),它會回傳 403 Forbidden。
-
-
標注: (需添加 Config) - 你需要配置
AuthenticationEntryPoint
和AccessDeniedHandler
來告訴這個 Filter,當錯誤發生時,回傳的 JSON 格式應該長什麼樣子。
-
-
AuthorizationFilter
(授權過濾器)-
工作: 極度重要! 這是「最後的守門人」。它會拿著你(在
JwtAuthenticationFilter
中)放進SecurityContextHolder
的Authentication
(通行證),去核對你在SecurityConfig
中設定的規則(例如.requestMatchers("/api/admin/**").hasRole("ADMIN")
)。 -
如果權限不符,它會拋出
AccessDeniedException
(然後被第 7 步的ExceptionTranslationFilter
抓到)。 -
標注: (需添加 Config) - 你的
http.authorizeHttpRequests(...)
中的所有規則,都是在配置這個 Filter。
-