Spring Security 實踐之登錄

前言

Spring Security是一個功能強大且高度且可定制的身份驗證和訪問控制框架,包含標準的身份認證和授權。

本文主要介紹SpringBoot中如何配置使用 Spring Security 安全認證框架并簡述相關原理和步驟。

核心認證流程解析

在這里插入圖片描述

  1. 請求過濾
  • 用戶提交登錄表單
  • AbstractAuthenticationProcessingFilter 過濾請求,創建 AbstractAuthenticationToken

以默認提供的 UsernamePasswordAuthenticationFilter舉例:

public class UsernamePasswordAuthenticationFilter extendsAbstractAuthenticationProcessingFilter {public UsernamePasswordAuthenticationFilter() {// 設置過濾規則super(new AntPathRequestMatcher("/login", "POST"));}public Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException {// ......String username = obtainUsername(request);String password = obtainPassword(request);if (username == null) {username = "";}if (password == null) {password = "";}username = username.trim();// 根據請求參數設置 UsernamePasswordAuthenticationToken 實例UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);setDetails(request, authRequest);return this.getAuthenticationManager().authenticate(authRequest);}// ......
}
  1. 認證管理器
  • AuthenticationManager(通常是 ProviderManager)協調認證過程,根據 AbstractAuthenticationToken類型 選擇對應的 AuthenticationProvider

ProviderManager中根據不同的 Token 類型匹配不同的 Provider

public Authentication authenticate(Authentication authentication)throws AuthenticationException {Class<? extends Authentication> toTest = authentication.getClass();AuthenticationException lastException = null;AuthenticationException parentException = null;Authentication result = null;Authentication parentResult = null;boolean debug = logger.isDebugEnabled();// 遍歷所有已注冊的 providers ,匹配能處理當前 authentication 的 provider 進行認證處理for (AuthenticationProvider provider : getProviders()) {// 判斷provider是否可以處理當前的authenticationif (!provider.supports(toTest)) {continue;}try {// 執行認證邏輯,返回認證結果result = provider.authenticate(authentication);if (result != null) {copyDetails(authentication, result);break;}} catch (AccountStatusException | InternalAuthenticationServiceException e) {prepareException(e, authentication);throw e;} catch (AuthenticationException e) {lastException = e;}}// .......throw lastException;
}
  • 匹配到對應的 provider 后,調用 provider.authenticate(authentication);執行實際的認證過程。

:::tips
認證過程以 AbstractUserDetailsAuthenticationProvider(實現DaoAuthenticationProvider)為例,大致過程為:

  • getUserDetailsService().loadUserByUsername(username); 加載用戶信息
  • preAuthenticationChecks.check(user); 校驗用戶信息,是否鎖定、過期、可用等
  • additionalAuthenticationChecks(user,(UsernamePasswordAuthenticationToken) authentication); 驗證用戶密碼

:::

  1. 認證后處理
  • 認證成功:生成已認證的 Authentication 對象,存入 SecurityContext
  • 認證失敗:拋出 AuthenticationExceptionAuthenticationEntryPoint 處理
組件職責典型實現類
AbstractAuthenticationProcessingFilter攔截認證請求,封裝認證對象UsernamePasswordAuthenticationFilter
AuthenticationManager認證流程協調者ProviderManager
AuthenticationProvider執行具體認證邏輯DaoAuthenticationProvider
UserDetailsService加載用戶數據自定義實現類

其中 UsernamePasswordAuthenticationFilterUsernamePasswordAuthenticationToken為框架自帶的 登錄請求過濾和Token實例。

如需自定義登錄請求過濾和Token實例,可自行實現 AbstractAuthenticationProcessingFilterAbstractAuthenticationToken接口。

不同的 AbstractAuthenticationToken 通常有不同的 AuthenticationProvider與之對應,用于實現不同的認證邏輯。

自定義的認證邏輯中,通常都是對 AbstractAuthenticationTokenAuthenticationProvider 的不同實現。

JWT

JWT(JSON Web Token) 是一種輕量級的開放標準(RFC 7519),用于在網絡應用間安全地傳輸信息。它通常用于 身份認證(Authentication) 和 數據交換(Information Exchange),特別適合 前后端分離 和 無狀態(Stateless) 的應用場景。

JWT在登錄中的應用過程

  1. 用戶提交 用戶名 + 密碼 登錄
  2. 服務器驗證后,生成 JWT 并返回給客戶端
  3. 客戶端存儲 JWT(通常放 localStorageCookie
  4. 后續請求 在 Authorization 頭攜帶 JWT
  5. 服務器 驗證 JWT 簽名,并解析數據

特點

  • 防篡改:簽名(Signature)確保 Token 未被修改
  • 可設置有效期(exp 字段)避免長期有效
  • 無狀態:服務器不需要存儲 Session,適合分布式系統

自定義認證邏輯


接下來根據 Spring Security 的認證過程和JWT的特點進行自定義登錄邏輯的編寫

核心類圖

在這里插入圖片描述

登錄流程

在這里插入圖片描述

具體實現

JwtLoginFilter

實現 AbstractAuthenticationProcessingFilter接口

public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter {public JwtLoginFilter() {// 設置當前 Filter ,也就是需要過濾的登錄URLsuper(new AntPathRequestMatcher("/auth/login", "POST"));}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)throws AuthenticationException, IOException, ServletException {// 獲取前端傳遞登錄模式String loginType = request.getParameter("loginType");Authentication authentication = null;// 判斷前端使用的登錄模式if (CommonConstant.LoginType.SMS.equals(loginType)) {// 手機短信String phone = request.getParameter("phone");String code = request.getParameter("code");authentication = new SmsAuthenticationToken(phone, code);}if (CommonConstant.LoginType.WX.equals(loginType)) {String code = request.getParameter("code");authentication = new WxAuthenticationToken(code);}if (authentication == null) {throw new UnsupportedLoginTypeException();}return getAuthenticationManager().authenticate(authentication);}
}

SmsAuthenticationToken

TIPS:如果需要多種認證模式,如:用戶密碼、短信認證、掃描登錄、三方認證等,可實現不同的 Token實例,并實現與之對應的 Provider

以短信認證Token SmsAuthenticationToken舉例

public class SmsAuthenticationToken extends AbstractAuthenticationToken {private final String phone;private final String code;public SmsAuthenticationToken(String phone, String code) {super(new ArrayList<>());this.phone = phone;this.code = code;}@Overridepublic String getCredentials() {return code;}@Overridepublic String getPrincipal() {return phone;}
}

SmsAuthProvider

具體實現短信認證的邏輯,主要工作原理是將前端傳遞的手機號和短信驗證碼進行匹配校驗,如果合法,則認證成功,如果不合法,返回認證失敗。

@Component
public class SmsAuthProvider implements AuthenticationProvider {@Autowiredprivate AbstractLogin abstractLogin;@Autowiredprivate JwtUtil jwtUtil;/*** 驗證手機驗證碼登錄認證** @param authentication the authentication request object.* @return* @throws AuthenticationException*/@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {SmsAuthenticationToken token = (SmsAuthenticationToken) authentication;String phone = token.getPrincipal();String code = token.getCredentials();try {UserDetails userDetails = abstractLogin.smsLogin(phone, code);token.setDetails(userDetails);} catch (AuthenticationException authenticationException) {throw authenticationException;} catch (Exception e) {throw new LoginFailException();}return token;}@Overridepublic boolean supports(Class<?> authentication) {return SmsAuthenticationToken.class.equals(authentication);}
}

SpringBoot配置過程


我們已在上述的過程中將核心的認證邏輯實現,接下來就是把對應的代碼配置到 Spring Security 工程之中。

JwtAuthConfig

JwtAuthConfig實現 WebSecurityConfigurerAdapter作為整體的配置入口

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class JwtAuthConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate JwtLoginConfig loginConfig;@Overridepublic void configure(WebSecurity web) throws Exception {super.configure(web);}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().formLogin().disable()// 應用登錄相關配置信息.apply(loginConfig).and().authorizeRequests()// 放行登錄 URL .antMatchers("/auth/login").permitAll();}
}

該配置類中,只做了初始化的簡單配置,如設置放行登錄URL、禁用 csrf、禁用 默認的formLogin等。更多的登錄認證配置在 JwtLoginConfig中進行。

JwtLoginConfig

JwtLoginConfig實現了SecurityConfigurerAdapterSecurityConfigurerAdapter 是 Spring Security 的核心配置基類,用于自定義安全規則(如認證、授權、過濾器鏈等)。

配置信息如下:

@Configuration
public class JwtLoginConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {@Autowiredprivate LoginSuccessHandler successHandler;@Autowiredprivate LoginFailHandler failHandler;@Autowiredprivate JwtProviderManager jwtProviderManager;/*** 將登錄接口的過濾器配置到過濾器鏈中* 1. 配置登錄成功、失敗處理器* 2. 配置自定義的userDetailService(從數據庫中獲取用戶數據)* 3. 將自定義的過濾器配置到spring security的過濾器鏈中,配置在UsernamePasswordAuthenticationFilter之前* @param http*/@Overridepublic void configure(HttpSecurity http) {JwtLoginFilter filter = new JwtLoginFilter();// authenticationManager 中已經預設系統內的 provider 集合filter.setAuthenticationManager(jwtProviderManager);//認證成功處理器filter.setAuthenticationSuccessHandler(successHandler);//認證失敗處理器filter.setAuthenticationFailureHandler(failHandler);//將這個過濾器添加到UsernamePasswordAuthenticationFilter之后執行http.addFilterAfter(filter, UsernamePasswordAuthenticationFilter.class);}
}

JwtProviderManager

從上文中的認證過程(時序圖)中,AuthenticationManager 是委托 ProviderManager進行認證模式的匹配和執行對應的 provider。

:::tips
為了后續的多認證模式的支持和動態匹配,所以將 ProviderManager 交給 Spring 容器管理,并且通過構造方法將平臺內所有已經注冊到Spring容器中的 provider進行注入,以達到自動裝配的目的。

注:暫只做簡單實現。

:::

@Component
public class JwtProviderManager extends ProviderManager {public JwtProviderManager(List<AuthenticationProvider> providers) {super(providers);}
}

LoginSuccessHandler

認證成功后,對認證結果生成JWT Token 返回前端

public class LoginSuccessHandler implements AuthenticationSuccessHandler {@Autowiredprivate JwtUtil jwtUtil;@Autowiredprivate NacosHcUserConfigProperties configProperties;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request,HttpServletResponse response,Authentication authentication)throws IOException, ServletException {// 生成 token 返回前端
//        Object principal = authentication.getPrincipal();UserDetails details = (UserDetails) authentication.getDetails();// accessToken 過期時間 30分鐘Long accessTokenExpireSeconds = configProperties.getAuth().getAccessTokenExpireSeconds();// refreshToken 過期時間 6小時Long refreshTokenExpireSeconds = configProperties.getAuth().getRefreshTokenExpireSeconds();String accessToken = jwtUtil.createToken(details.getUsername(), accessTokenExpireSeconds);String refreshToken = jwtUtil.createToken(accessToken, refreshTokenExpireSeconds);Map<String, String> tokenMap = new HashMap<>();tokenMap.put("accessToken", accessToken);tokenMap.put("refreshToken", refreshToken);response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);response.getWriter().write(JSON.toJSONString(ApiResult.success(tokenMap)));}
}

END

至此,相關 Spring Security 配置已完成!💯💯

🧑?💻🧑?💻🧑?💻

下一篇繼續探究 Spring Security 在登錄后的認證鑒權過程。

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

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

相關文章

華為云開發者空間 × DeepSeek-R1 智能融合測評:云端開發與AI客服的協同進化

前言&#xff1a; 華為開發者空間&#xff0c;是為全球開發者打造的專屬開發者空間&#xff0c;致力于為每位開發者提供一臺云主機、一套開發工具和云上存儲空間&#xff0c;當普惠云資源遇見推理大模型&#xff0c;企業服務與開發效能的范式革命正在加速。華為云開發者空間&am…

二分查找----4.搜索旋轉排序數組

題目鏈接 /** 升序數組在某個位置被分割為前后兩部分,前后兩部分整體互換;在被改變后的數組中找到目標值 O(log n)---> 二分查找 特點: 旋轉后的數組被分割為兩個獨立的遞增區間 左半區的最小值,大于右半區的最大值(mid所在區間的判斷依據) 二分策略: 首先判斷mid落在左區間…

地球表面附近兩點之間距離、高低角和方位角的計算方法,VC++代碼實操!

書接上文&#xff0c;這篇文章介紹具體的VC編程實現&#xff0c;代碼實操。任何一個算法&#xff0c;你必須將其編寫為代碼&#xff0c;運行結果正確&#xff0c;才算真正掌握了&#xff0c;否則都是似懂非懂&#xff0c;一知半解&#xff0c;下面先給出仿真結果的截圖&#xf…

uniapp各大平臺導航組件

最近有個需求要點擊導航然后跳出各家導航軟件話不多出直接貼出代碼&#xff1a;這個可以作為組件引入<template><view><view class"nav" :style"{color: customColor}" click.stop"openMap">{{title}}</view><!-- 彈…

Access開發一鍵刪除Excel指定工作表

Hi&#xff0c;大家好&#xff01;又到了每周給大家更新的時間了&#xff0c;這周給大家講講excel的處理操作吧。在開始前&#xff0c;先給大家匯報一下我們框架的進度&#xff0c;最近兩周沒有直播&#xff0c;所以大家不太清楚目前的進度&#xff0c;框架目前就差權限了&…

無廣告終端安全產品推薦:打造純凈辦公環境的安全之選

在數字化辦公時代&#xff0c;終端安全防護是企業和個人不可忽視的重要環節。然而&#xff0c;許多傳統安全軟件往往伴隨著頻繁的廣告彈窗和推廣信息&#xff0c;不僅干擾正常工作&#xff0c;還可能成為潛在的安全隱患。本文將為您介紹幾款「無廣告、無捆綁」的終端產品&#…

使用UE5自帶節點InteriorCubemap制作假室內效果

Interior Mapping&#xff08;室內映射&#xff09;是一種用著色器方法模擬室內結構紋理的方式&#xff0c;避免了真實對室內場景建模造成的模型面數渲染開銷&#xff0c;在《蜘蛛俠》《城市天際線》等游戲中都采用了該技術。 UE自帶了節點InteriorCubemap&#xff08;Unity S…

基于單片機睡眠質量/睡眠枕頭設計

傳送門 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目速選一覽表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目功能速覽 概述 隨著現代社會生活節奏的加快&#xff0c;睡眠質量問題日益受到人們的關注。本研究設計了一種基于…

Ajax第一天

AJAX概念&#xff1a;AJAX 是瀏覽器與服務器進行數據通信的技術&#xff08;把數據變活&#xff09;語法&#xff1a;1.引入 axios.js&#xff1a;https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js2.使用 axios 函數? 傳入配置對象? 再用 .then 回調函數接收結果&#…

AI大模型各類概念掃盲

以下內容整理自AI&#xff0c;進行一個概念掃盲&#xff1a;Prompt&#xff08;提示詞&#xff09; Prompt是用戶提供給AI模型的指令或問題&#xff0c;用于引導模型生成特定輸出。良好的Prompt設計能顯著提升模型的任務理解能力和響應質量&#xff0c;例如通過結構化提示&…

Linux系統編程——網絡

一、TCP/UDP 1、osi模型 物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層、應用層&#xff08;下層為上層提供服務&#xff09; 2、TCP/IP模型&#xff08;TCP/IP協議棧&#xff09; 應用層&#xff1a; HTTP&#xff08;超文本傳輸協議&#xff09;、FTP&#xff08;文件…

taro+pinia+小程序存儲配置持久化

主要通過taro的getStorageSync,setStorageSync實現配置持久化 // https://pinia.esm.dev/introduction.html import { defineStore } from pinia; import { CreditCardDateUtils } from /untils/compute; import { getStorageSync, setStorageSync } from "tarojs/taro&qu…

抖音小游戲好做嗎?

從0到1&#xff0c;教你打造爆款抖音小游戲隨著移動互聯網的發展&#xff0c;抖音小游戲憑借便捷即玩、流量龐大等優勢&#xff0c;成為游戲開發者的熱門選擇。想知道如何開發出一款吸睛又好玩的抖音小游戲嗎&#xff1f;下面就為你詳細介紹開發流程。一、前期規劃明確游戲類型…

Spring Boot 3核心技術面試指南:從遷移升級到云原生實戰,9輪技術攻防(含架構解析)

面試官&#xff1a;cc程序員&#xff0c;聊聊Spring Boot 3的那些事兒&#xff1f; 場景背景 互聯網大廠云原生架構部面試官老王&#xff0c;與自稱"Spring Boot骨灰粉"的cc程序員展開技術對決。 面試過程 第一輪&#xff1a;遷移升級 面試官&#xff1a;Spring Boot…

技術演進中的開發沉思-42 MFC系列:Components 與 ActiveX Controls

點擊程序啟動時&#xff0c;是不是看過有加載的畫面。在VC開發時&#xff0c;可使用 VC 的 Component Gallery&#xff0c;找到 Splash screen 組件&#xff0c;當時覺得組件就是給程序員的暖手寶。一、Component GalleryComponent Gallery 在 VC 里的位置很特別 —— 它藏在 “…

抽象類、接口、枚舉

第八天&#xff08;堅持&#xff09;抽象類1.什么是抽象類&#xff0c;作用特點。抽象類是面向對象編程中一種特殊的類&#xff0c;它不能被實例化&#xff0c;主要用于作為其他類的基類&#xff08;父類&#xff09;。抽象類的主要作用是定義公共結構和行為規范&#xff0c;同…

在Ubuntu上使用QEMU仿真運行ARM匯編

ARM匯編一般無法在PC上直接運行&#xff0c;因為ARM和x86架構是不一樣的。但是很多時候用ARM開發板是很不方便的&#xff0c;所以能不能直接在PC上仿真運行ARM匯編來練習呢&#xff1f;當然可以&#xff0c;那就是&#xff1a;使用QEMU來仿真。這篇文章我們就來演示下如何在Ubu…

【趣味解讀】淘寶登錄的前后端交互機制:Cookie-Session 如何保障你的賬戶安全?

在現代Web應用中&#xff0c;前后端交互是核心功能之一&#xff0c;而用戶認證又是其中最關鍵的部分。本文將以淘寶登錄為例&#xff0c;詳細解析基于Cookie-Session的前后端交互流程&#xff0c;幫助開發者理解這一常見的安全認證機制。生動理解一下什么是cookie和seesion我們…

貪心算法(基礎算法)

1.引言 ok啊&#xff0c;拖更這么長時間也是沒有壓力&#xff08;doge&#xff09; 不說啥&#xff0c;直接進入正題。 2.概念 這個貪心算法呢&#xff0c;看名字就知道&#xff0c;不就是每個步驟都挑最好的嘛&#xff0c;有啥難的。 這么說的話......其實確實&#xff0c…

簡單的mcp 服務示例

參考&#xff1a;https://www.bilibili.com/video/BV1nyVDzaE1x 編寫自己的tools.py #### tools.py from pathlib import Path import osbase_dir Path("./test")def read_file(name: str) -> str:"""Return file content. If not exist, return …