Spring Security認證授權深度解析

一 Spring Security簡介

Spring Security是Spring生態系統中的一個安全框架,主要用于處理認證(Authentication)和授權(Authorization)。它提供了一套完整的安全解決方案,可以輕松集成到Spring應用中。

二 核心概念

1. 認證(Authentication)

驗證用戶的身份,確認"你是誰"。例如:用戶登錄過程。

2. 授權(Authorization)

驗證用戶是否有權限執行某個操作,確認"你能做什么"。例如:檢查用戶是否有權訪問某個API。

3. 主要組件

  • SecurityContextHolder:存儲安全上下文信息
  • Authentication:存儲當前用戶的認證信息
  • UserDetails:用戶信息的核心接口
  • UserDetailsService:加載用戶信息的核心接口
  • AuthenticationProvider:認證的具體實現者

三 實戰案例:基于JWT的認證授權系統

一、核心架構

1. 核心組件關系圖

請求 → SecurityFilterChain → (多個Security Filter) → 目標資源↓SecurityContextHolder↓SecurityContext↓Authentication/            \Principal     GrantedAuthority

2. 核心組件說明

2.1 SecurityContextHolder
  • 作用:存儲當前線程的安全上下文信息
  • 實現:使用ThreadLocal存儲SecurityContext
  • 訪問方式:
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
2.2 SecurityContext
  • 作用:持有Authentication對象和其他安全相關信息
  • 生命周期:請求開始到結束
  • 存儲位置:ThreadLocal或Session中
2.3 Authentication
  • 作用:存儲用戶認證信息
  • 主要屬性:
    • principal:用戶身份信息
    • credentials:憑證信息(如密碼)
    • authorities:用戶權限集合
    • authenticated:是否已認證
  • 常用實現:UsernamePasswordAuthenticationToken

二、認證流程詳解

1. 完整認證流程圖

用戶請求登錄↓
UsernamePasswordAuthenticationFilter↓
AuthenticationManager↓
AuthenticationProvider↓
UserDetailsService↓
UserDetails↓
Authentication對象↓
SecurityContext

2. 詳細流程說明

2.1 認證入口(以登錄為例)
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {// 1. 創建未認證的AuthenticationAuthentication authentication = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(),loginRequest.getPassword());// 2. 執行認證Authentication authenticated = authenticationManager.authenticate(authentication);// 3. 認證成功,生成JWTSecurityContextHolder.getContext().setAuthentication(authenticated);String jwt = tokenProvider.generateToken(authenticated);return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
}
2.2 認證管理器(AuthenticationManager)
public class ProviderManager implements AuthenticationManager {private List<AuthenticationProvider> providers;@Overridepublic Authentication authenticate(Authentication authentication) {// 遍歷所有Provider嘗試認證for (AuthenticationProvider provider : providers) {if (!provider.supports(authentication.getClass())) {continue;}try {return provider.authenticate(authentication);} catch (AuthenticationException e) {// 處理認證異常}}throw new AuthenticationException("無法認證");}
}
2.3 自定義認證提供者
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {private final CustomUserDetailsService userDetailsService;private final PasswordEncoder passwordEncoder;@Overridepublic Authentication authenticate(Authentication authentication) {// 1. 獲取認證信息String username = authentication.getName();String password = authentication.getCredentials().toString();// 2. 加載用戶信息UserDetails userDetails = userDetailsService.loadUserByUsername(username);// 3. 驗證密碼if (!passwordEncoder.matches(password, userDetails.getPassword())) {throw new BadCredentialsException("密碼錯誤");}// 4. 創建已認證的Authenticationreturn new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());}
}

三、授權流程詳解

1. 授權流程圖

請求 → FilterSecurityInterceptor↓SecurityContextHolder獲取Authentication↓AccessDecisionManager↓AccessDecisionVoter↓權限判斷結果

2. 詳細授權步驟

2.1 配置安全規則
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) {http.authorizeRequests()// 1. URL級別的權限控制.antMatchers("/api/public/**").permitAll().antMatchers("/api/admin/**").hasRole("ADMIN")// 2. 自定義權限判斷.anyRequest().access("@customSecurityService.hasPermission(request,authentication)");}
}
2.2 方法級別權限控制
@Service
public class UserService {// 使用Spring EL表達式進行權限控制@PreAuthorize("hasRole('ADMIN') or #username == authentication.name")public UserDetails getUser(String username) {// 方法實現}
}

四、JWT集成原理

1. JWT認證流程

請求 → JwtAuthenticationFilter↓提取JWT令牌↓驗證JWT有效性↓解析用戶信息↓創建Authentication↓存入SecurityContext

2. JWT過濾器實現

public class JwtAuthenticationFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain filterChain) {try {// 1. 從請求中提取JWTString jwt = getJwtFromRequest(request);if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {// 2. 從JWT中獲取用戶信息String username = tokenProvider.getUsernameFromJWT(jwt);String roles = tokenProvider.getRolesFromJWT(jwt);// 3. 創建AuthenticationList<GrantedAuthority> authorities = Arrays.stream(roles.split(",")).map(SimpleGrantedAuthority::new).collect(Collectors.toList());UsernamePasswordAuthenticationToken authentication =new UsernamePasswordAuthenticationToken(username, null, authorities);// 4. 設置認證信息SecurityContextHolder.getContext().setAuthentication(authentication);}} catch (Exception ex) {logger.error("無法設置用戶認證", ex);}filterChain.doFilter(request, response);}
}

五、數據校驗流程

1. 請求數據校驗

@PostMapping("/login")
public ResponseEntity<?> login(@Valid @RequestBody LoginRequest request) {// 1. @Valid觸發數據校驗// 2. 校驗失敗拋出MethodArgumentNotValidException
}public class LoginRequest {@NotBlank(message = "用戶名不能為空")private String username;@NotBlank(message = "密碼不能為空")private String password;
}

2. 認證數據校驗

public class CustomUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) {// 1. 檢查用戶是否存在UserPrincipal user = userMap.get(username);if (user == null) {throw new UsernameNotFoundException("用戶不存在");}// 2. 檢查用戶狀態if (!user.isEnabled()) {throw new DisabledException("用戶已禁用");}// 3. 檢查賬戶是否過期if (!user.isAccountNonExpired()) {throw new AccountExpiredException("賬戶已過期");}// 4. 檢查賬戶是否鎖定if (!user.isAccountNonLocked()) {throw new LockedException("賬戶已鎖定");}return user;}
}

3. JWT數據校驗

public class JwtTokenProvider {public boolean validateToken(String authToken) {try {// 1. 驗證簽名Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);// 2. 驗證是否過期Claims claims = getClaimsFromJWT(authToken);return !claims.getExpiration().before(new Date());} catch (SignatureException ex) {logger.error("無效的JWT簽名");} catch (MalformedJwtException ex) {logger.error("無效的JWT令牌");} catch (ExpiredJwtException ex) {logger.error("JWT令牌已過期");} catch (UnsupportedJwtException ex) {logger.error("不支持的JWT令牌");} catch (IllegalArgumentException ex) {logger.error("JWT聲明為空");}return false;}
}

六、異常處理流程

1. 認證異常處理

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request,HttpServletResponse response,AuthenticationException e) throws IOException {// 1. 未認證異常處理response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);response.setContentType(MediaType.APPLICATION_JSON_VALUE);String message = "請先進行認證";if (e instanceof BadCredentialsException) {message = "用戶名或密碼錯誤";} else if (e instanceof JwtExpiredTokenException) {message = "token已過期";}response.getWriter().write(new ObjectMapper().writeValueAsString(new ApiResponse(false, message)));}
}

2. 授權異常處理

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request,HttpServletResponse response,AccessDeniedException e) throws IOException {// 1. 權限不足異常處理response.setStatus(HttpServletResponse.SC_FORBIDDEN);response.setContentType(MediaType.APPLICATION_JSON_VALUE);response.getWriter().write(new ObjectMapper().writeValueAsString(new ApiResponse(false, "沒有足夠的權限")));}
}

七、安全上下文傳遞

1. 異步方法中的安全上下文

@Async
public CompletableFuture<String> asyncMethod() {// 1. 獲取當前安全上下文SecurityContext context = SecurityContextHolder.getContext();return CompletableFuture.supplyAsync(() -> {try {// 2. 設置安全上下文到新線程SecurityContextHolder.setContext(context);// 3. 執行業務邏輯return "success";} finally {// 4. 清理安全上下文SecurityContextHolder.clearContext();}});
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/75346.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/75346.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/75346.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[學成在線]10-課程審核

課程審核 需求分析 根據模塊需求分析&#xff0c;課程發布前要先審核&#xff0c;審核通過方可發布。下圖是課程審核及發布的流程圖&#xff1a; 為什么課程審核通過才可以發布呢&#xff1f; 這樣做為了防止課程信息有違規情況&#xff0c;課程信息不完善對網站用戶體驗也不…

【SpringBoot + MyBatis + MySQL + Thymeleaf 的使用】

目錄&#xff1a; 一&#xff1a;創建項目二&#xff1a;修改目錄三&#xff1a;添加配置四&#xff1a;創建數據表五&#xff1a;創建實體類六&#xff1a;創建數據接口七&#xff1a;編寫xml文件八&#xff1a;單元測試九&#xff1a;編寫服務層十&#xff1a;編寫控制層十一…

Elasticsearch索引的字段映射

目錄 type String類型 text和keyword的區別 數值類型 日期類型 index doc_values fields analyzer store index_options ElasticSearch索引映射示例 Elasticsearch中的字段設置直接影響數據的存儲、索引和查詢行為。結合索引查詢場景合理設置mapping信息可以起到提…

【已解決】實際參數列表和形式參數列表長度不同、java: 無法將類xxx中的構造器xxx應用到給定類型| lombok相關

idea運行maven項目時&#xff0c;報錯這個&#xff08;如標題&#xff09; 解決方案記錄&#xff1a; 找到了之前的、能運行成功不報錯的 maven項目。參考其pom.xml文件中lombok相關部分&#xff0c;將<path>標簽下的lombok加個版本號&#xff0c;就運行成功了&#xff1…

4. 學習網站:學習新的技能或培養興趣愛好

文章目錄 前言英文網站&#xff1a;培養興趣愛好的應用總結 前言 學習網站以及具體提供的內容。 英文網站&#xff1a; Coursera&#xff1a;提供來自全球頂尖大學和機構的在線課程&#xff0c;涵蓋廣泛的學科&#xff0c;包括編程、數據科學、商業和藝術等。Udemy&#xff1…

LabVIEW 開發中 TCP 與 UDP 協議的差異

在 LabVIEW 開發的網絡通信場景中&#xff0c;TCP 和 UDP 是常用的兩種傳輸層協議&#xff0c;它們各自具有獨特的特點&#xff0c;適用于不同的應用需求。理解它們之間的差異&#xff0c;有助于開發者根據具體項目需求選擇合適的協議&#xff0c;以實現高效、穩定的網絡通信。…

04-深入解析 Spring 事務管理原理及源碼

深入解析 Spring 事務管理原理及源碼 Spring 事務管理&#xff08;Transaction Management&#xff09;是企業級應用開發中至關重要的功能之一&#xff0c;它確保數據操作的 原子性、一致性、隔離性、持久性&#xff08;ACID&#xff09;。 本篇博客將從 Spring 事務的基本概…

【Linux】用戶向硬件寄存器寫入值過程理解

思考一下&#xff0c;當我們咋用戶態向寄存器寫入一個值&#xff0c;這個過程是怎么樣的呢&#xff1f;以下是應用程序通過標準庫函數&#xff08;如 write()、ioctl() 或 mmap()&#xff09;向硬件寄存器寫入值的詳細過程&#xff0c;從用戶空間到內核再到硬件的完整流程&…

自動駕駛02:點云預處理——02(運動補償篇LIO-SAM)

當激光雷達&#xff08;LiDAR&#xff09;在運動中采集點云時&#xff0c;每個點的時間戳不同&#xff0c;而車輛在移動&#xff0c;導致點云在不同時間點的坐標與實際情況不符&#xff0c;這種現象稱為運動畸變&#xff08;Motion Distortion&#xff09;。為了得到無畸變的點…

基礎算法篇(3)(藍橋杯常考點)—圖論

前言 這期是基礎算法篇的第三節&#xff0c;其中的dijkstra算法更是藍橋杯中的高頻考點 圖的基本相關概念 有向圖和無向圖 自環和重邊 稠密圖和稀疏圖 對于不帶權的圖&#xff0c;一條路徑的路徑長度是指該路徑上各邊權值的總和 對于帶權的圖&#xff0c;一條路徑長度時指該路…

Crawl4AI:專為AI設計的開源網頁爬蟲工具,釋放大語言模型的潛能

在當今數據驅動的AI時代,高效獲取結構化網頁數據是模型訓練和應用落地的關鍵。Crawl4AI作為一款專為大型語言模型(LLMs)設計的開源爬蟲工具,憑借其極速性能、AI友好輸出和模塊化設計,正在成為開發者社區的熱門選擇。本文將深入解析其核心特性與技術優勢。 一、Crawl4AI的核…

前后端數據序列化:從數組到字符串的旅程(附優化指南)

&#x1f310; 前后端數據序列化&#xff1a;從數組到字符串的旅程&#xff08;附優化指南&#xff09; &#x1f4dc; 背景&#xff1a;為何需要序列化&#xff1f; 在前后端分離架構中&#xff0c;復雜數據類型&#xff08;如數組、對象&#xff09;的傳輸常需序列化為字符…

匯編學習之《移位指令》

這章節學習前需要回顧之前的標志寄存器的內容&#xff1a; 匯編學習之《標志寄存器》 算數移位指令 SAL (Shift Arithmetic Left)算數移位指令 : 左移一次&#xff0c;最低位用0補位&#xff0c;最高位放入EFL標志寄存器的CF位&#xff08;進位標志&#xff09; OllyDbg查看…

NLP高頻面試題(二十九)——大模型解碼常見參數解析

在大語言模型的實際應用中&#xff0c;如何更有效地控制文本生成的質量與多樣性&#xff0c;一直是熱門研究話題。其中&#xff0c;模型解碼&#xff08;decode&#xff09;策略至關重要&#xff0c;涉及的主要參數包括 top_k、top_p 和 temperature 等。本文將詳細介紹這些常見…

【C#】Task 線程停止

CancellationTokenSource cts 是用于控制任務&#xff08;線程&#xff09;停止運行的。我們一步步來解釋它的作用。 &#x1f50d; 現在的代碼結構大概是這樣的&#xff1a; Task.Run(() > {while (true){// 不斷循環采集圖像} });這種寫法雖然簡單&#xff0c;但最大的問…

WebRTC的ICE之TURN協議的交互流程中繼轉發Relay媒體數據的turnserver的測試

WebRTC的ICE之TURN協議的交互流程和中繼轉發Relay媒體數據的turnserver的測試 WebRTC的ICE之TURN協議的交互流程中繼轉發Relay媒體數據的turnserver的測試 WebRTC的ICE之TURN協議的交互流程和中繼轉發Relay媒體數據的turnserver的測試前言一、TURN協議1、連接Turn Server 流程①…

Redis + Caffeine多級緩存電商場景深度解析

Redis Caffeine多級緩存 Redis Caffeine多級緩存電商場景深度解析一、實施目的二、具體實施2.1 架構設計2.2 組件配置2.3 核心代碼實現 三、實施效果3.1 性能指標對比3.2 業務指標改善3.3 系統穩定性 四、關鍵策略4.1 緩存預熱4.2 一致性保障4.3 監控配置Prometheus監控指標 …

前端開發3D-基于three.js

基于 three.js 渲染任何畫面&#xff0c;都要基于這 3 個要素來實現 1場景scene&#xff1a;放置物體的容器 2攝像機&#xff1a;類似人眼&#xff0c;可調整位置&#xff0c;角度等信息&#xff0c;展示不同畫面 3渲染器&#xff1a;接收場景和攝像機對象&#xff0c;計算在瀏…

代碼隨想錄算法訓練營--打卡day4

一.移除鏈表元素 1.題目鏈接 203. 移除鏈表元素 - 力扣&#xff08;LeetCode&#xff09; 2.思路 通過 while 循環來遍歷鏈表&#xff0c;只要 cur 的下一個節點不為空&#xff0c;就繼續循環。在循環中&#xff0c;對 cur 的下一個節點的值進行判斷&#xff1a; 值不等于…

虛擬電廠:多元能源聚合,開啟綠色電力新時代

虛擬電廠&#xff1a;多元能源聚合&#xff0c;開啟綠色電力新時代 在“雙碳”目標驅動下&#xff0c;電力系統正經歷從集中式向分布式、從單一能源向多能互補的深刻變革。 作為能源互聯網的核心載體&#xff0c;虛擬電廠通過數字化技術整合多種能源資源&#xff0c;而是像指…