開源項目XBuilder的user邏輯

  • stores \ user
    • query-keys.ts 統一管理Vue Query(TanStack Query的Vue適配版本)緩存鍵,在下面的文件中復用
    • index.ts 入口文件,統一用戶信息查詢
    • signed-in.ts 登錄狀態管理、認證邏輯

在用戶登錄后,系統頒發一個令牌,用于在后續的請求中證明“我是誰”

signed-in.js

const casdoorAuthRedirectPath = ‘/signed-in/callback’

定義認證回調路徑,用戶認證成功后重定向到此路徑

const casdoorSdk = new Sdk({

…casdoorConfig,

redirectPath: casdoorAuthRedirectPath

})

創建 Casdoor SDK 實例,展開配置并設置重定向路徑

const userStateStorageKey = ‘spx-user’

定義本地存儲鍵名,用于持久化用戶狀態

const userState = reactive({ // 返回Proxy代理對象,即所有屬性都變為響應式

accessToken: null as string | null, // 訪問令牌,用于API調用認證

// TS的類型注解語法,顯式聲明屬性的類型

初始化為 null ,然后后面只能改為類型為 string 或 null 類型的值

accessTokenExpiresAt: null as number | null, // 令牌過期時間戳:只能變成number類型

refreshToken: null as string | null // 刷新令牌,用于獲取新的訪問令牌

})

export function initUserState() {const stored = localStorage.getItem(userStateStorageKey)if (stored != null) {Object.assign(userState, JSON.parse(stored))}watchEffect(() => localStorage.setItem(userStateStorageKey, JSON.stringify(userState)))
}

持久化:從本地 localStorage 中恢復登錄狀態,從而減少沒必要的登錄請求和響應,提升用戶體驗,并在離線狀態下使用某些功能

并用 watchEffect 監聽狀態變化,自動同步到本地存儲

interface TokenResponse {access_token: stringexpires_in: numberrefresh_token: string
}

定義了令牌響應接口,描述了從認證服務器返回的令牌數據結構

(接口是一個契約,定義了對象有哪些對象以及分別是什么類型,而類定義了對象是如何創建、有哪些行為,提供了運行時邏輯)

function handleTokenResponse(resp: TokenResponse) {userState.accessToken = resp.access_tokenuserState.accessTokenExpiresAt = resp.expires_in ? Date.now() + resp.expires_in * 1000 : nulluserState.refreshToken = resp.refresh_token
}

令牌處理函數(令牌響應)

更新用戶的認證令牌

計算絕對過期時間:當前時間+有效期(記得乘以1000

更新用戶的刷新令牌

export function initiateSignIn(returnTo: string = window.location.pathname + window.location.search + window.location.hash
) {// Workaround for casdoor-js-sdk not supporting override of `redirectPath` in `signin_redirect`.const casdoorSdk = new Sdk({...casdoorConfig,redirectPath: `${casdoorAuthRedirectPath}?returnTo=${encodeURIComponent(returnTo)}`})casdoorSdk.signin_redirect()
}

發起登錄流程(returnTo屬性名稱:string類型 =賦值 當前頁面的路徑名+查詢參數+哈希值拼接結果,其中 window.location是瀏覽器環境的全局對象,表示當前頁面的 URL信息)

創建新的SDK實例

并通過 encodeURIComponent 對URI進行編碼防止XSS攻擊

通過新的SDK實例實現動態重定向

其中signin_redirect函數定義為

    async signin_redirect(additionalParams) {window.location.assign(this.pkce.authorizeUrl(additionalParams));}

通過掛載在 window.location 上的 assign 方法將用戶的瀏覽器重定向到指定的 URL ,后面的 PKCE (Proof Key for Code Exchange) 授權是為了幫助客戶端安全地獲取訪問令牌

export async function completeSignIn() {const resp = await casdoorSdk.exchangeForAccessToken()handleTokenResponse(resp)
}

完成登錄流程,通過授權碼交換獲取訪問令牌(函數在sdk.js中定義了),處理返回的令牌數據

export function signInWithAccessToken(accessToken: string) {userState.accessToken = accessTokenuserState.accessTokenExpiresAt = nulluserState.refreshToken = null
}
  • 使用現有訪問令牌直接登錄
  • 清空過期時間和刷新令牌(假設外部令牌管理)

登出函數

export function signOut() {userState.accessToken = nulluserState.accessTokenExpiresAt = nulluserState.refreshToken = null
}
  • 清空所有用戶狀態
  • 實現完全登出

令牌刷新機制

const tokenExpiryDelta = 60 * 1000 // 1 minute in milliseconds
let tokenRefreshPromise: Promise<string | null> | null = null
  • 定義令牌過期緩沖時間(1分鐘)
  • 防止并發刷新的 Promise 變量
export async function ensureAccessToken(): Promise<string | null> {if (isAccessTokenValid()) return userState.accessTokenif (tokenRefreshPromise != null) return tokenRefreshPromiseif (userState.refreshToken == null) {signOut()return null}tokenRefreshPromise = (async () => {try {const resp = await casdoorSdk.refreshAccessToken(userState.refreshToken!)handleTokenResponse(resp)} catch (e) {console.error('failed to refresh access token', e)throw e}if (!isAccessTokenValid()) {signOut()return null}return userState.accessToken})()return tokenRefreshPromise.finally(() => (tokenRefreshPromise = null))
}
  • 確保訪問令牌有效的核心函數
  • 如果令牌有效,直接返回
  • 防止并發刷新(通過 Promise 緩存)
  • 無刷新令牌時直接登出
  • 刷新失敗或新令牌無效時登出用戶
  • 使用 finally 清理 Promise 緩存

狀態檢查函數

function isAccessTokenValid(): boolean {return !!(userState.accessToken &&(userState.accessTokenExpiresAt === null || userState.accessTokenExpiresAt - tokenExpiryDelta > Date.now()))
}
  • 檢查訪問令牌是否有效
  • 令牌存在且未過期(考慮緩沖時間)
  • 使用雙重否定 !! 確保返回布爾值
export function isSignedIn(): boolean {return isAccessTokenValid() || userState.refreshToken != null
}
  • 檢查用戶是否已登錄
  • 有效訪問令牌或存在刷新令牌都視為已登錄

用戶信息獲取

export function getSignedInUsername(): string | null {if (!isSignedIn()) return nullif (!userState.accessToken) return nullconst decoded = jwtDecode<{ name: string }>(userState.accessToken)return decoded.name
}
  • 從 JWT 令牌中解析用戶名
  • 先檢查登錄狀態和令牌存在性
  • 使用泛型指定 JWT 載荷類型

查詢相關功能

const signedInUserStaleTime = 60 * 1000 // 1minexport function getSignedInUserQueryKey() {return [...getUserQueryKey(getSignedInUsername() ?? ''), 'signed-in']
}
  • 定義用戶信息緩存時間(1分鐘)
  • 生成查詢鍵,包含用戶名和 'signed-in' 標識
export function useSignedInUser() {const queryKey = computed(() => getSignedInUserQueryKey())return useQueryWithCache({queryKey: queryKey,async queryFn() {if (!isSignedIn()) return nullreturn apis.getSignedInUser()},failureSummaryMessage: {en: 'Failed to load signed-in user information',zh: '加載當前用戶信息失敗'},staleTime: signedInUserStaleTime})
}
  • Vue 組合式函數,用于獲取當前用戶信息
  • 使用計算屬性動態生成查詢鍵
  • 支持國際化錯誤消息
  • 設置緩存失效時間
export function useUpdateSignedInUser() {const queryCache = useQueryCache()return useAction(async function updateSignedInUser(params: apis.UpdateSignedInUserParams) {const updated = await apis.updateSignedInUser(params)queryCache.invalidate(getUserQueryKey(getSignedInUsername()!))return updated},{ en: 'Failed to update profile', zh: '更新個人信息失敗' })
}
  • Vue 組合式函數,用于更新用戶信息
  • 更新成功后使查詢緩存失效,觸發重新獲取
  • 使用非空斷言操作符 ! 確保用戶名存在
  • 支持國際化錯誤消息

總結

這個文件實現了一個完整的用戶認證系統,包括:

  1. 狀態管理: 使用 Vue 3 響應式系統管理用戶狀態
  2. 持久化: 自動同步狀態到本地存儲
  3. 認證流程: 支持 OAuth 2.0 授權碼流程
  4. 令牌管理: 自動刷新訪問令牌,防止并發刷新
  5. 錯誤處理: 完善的錯誤處理和用戶登出機制
  6. 緩存集成: 與查詢緩存系統集成,優化數據獲取
  7. 國際化: 支持中英文錯誤消息

代碼設計良好,考慮了安全性、性能和用戶體驗等多個方面。

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

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

相關文章

第十五章 SEO的簡單免費工具

SEO的基礎工具和檢測 前文中主要是講一些SEO的網站基本功&#xff0c;而在這一章那&#xff0c;會講到一些非常基本的工具&#xff0c;主要是關于&#xff1a;網站的流量、停留時長、關鍵詞密度、內容、以及Google的站長工具。 Google Search Console Google Search Console這是…

SSL 證書與 HTTPS 的關系:一文理清核心關聯

HTTPS&#xff08;Hypertext Transfer Protocol Secure&#xff09;和 SSL 證書&#xff08;Secure Sockets Layer Certificate&#xff09;是網絡安全的兩大基石&#xff0c;它們共同保障了互聯網通信的安全性和可信度。以下從定義、功能、關系及實際應用層面進行解析&#xf…

使用Jmeter參數化實現接口自動化測試

&#x1f345; 點擊文末小卡片&#xff0c;免費獲取軟件測試全套資料&#xff0c;資料在手&#xff0c;漲薪更快 本文記錄如何使用Jmeter參數化&#xff08;csv)實現接口自動化——測試Token不同入參情況下&#xff0c;接口請求能夠返回正確的結果1. 首先需要使用Jmeter獲取一個…

X-plore File Manager v4.34.02 修改版:安卓設備上的全能文件管理器

在使用安卓設備時&#xff0c;文件管理是日常操作中不可或缺的一部分。X-plore File Manager 作為一款功能強大的文件管理器&#xff0c;憑借其豐富的功能和便捷的操作&#xff0c;成為安卓用戶管理文件的首選工具之一。最新版 v4.34.02 修改版更是解鎖了更多高級功能&#xff…

React+threejs兩種3D多場景渲染方案

在現代 Web 開發中&#xff0c;3D 可視化需求日益增長&#xff0c;特別是在 React 生態系統中實現多 3D 場景的展示與交互。本文通過對比兩種實現方案&#xff0c;探討 React 中構建多 3D 場景的最佳實踐&#xff0c;分析它們的技術特點、性能表現和適用場景。方案一&#xff1…

React性能優化終極指南:memo、useCallback、useMemo全解析

掌握 React.memo、useCallback、useMemo 的正確使用姿勢&#xff0c;讓你的 React 應用性能飛起來&#xff01; &#x1f3af; React.memo 作用 React.memo 是一個高階組件&#xff0c;用于函數組件&#xff0c;通過淺比較 props 的變化來決定是否重新渲染。如果 props 沒有變…

借助 VR 消防技術開展應急演練,檢驗完善應急預案?

應急演練是企業應對火災事故的重要手段&#xff0c;而 VR 消防技術的應用&#xff0c;為應急演練帶來了全新的體驗和更高的效率。VR 消防技術通過虛擬現實技術模擬逼真的火災場景&#xff0c;讓參與者能夠身臨其境地感受火災發生時的緊張氛圍。某知名物流企業&#xff0c;倉庫眾…

【電賽學習筆記】MaxiCAM 項目實踐——二維云臺追蹤指定目標

前言 本文是對視覺模塊MaixCam實現二維云臺人臉跟蹤_嗶哩嗶哩_bilibili大佬的項目實踐整理與拓展&#xff0c;侵權即刪。 單路舵機基本控制 #導入必要模塊 from maix import pwm, time , pinmap#定義全局變量&#xff0c;設初值 SERVO_FREQ 50 #主頻 SERVO_MIN_DUT…

深入解析 ArkUI 觸摸事件機制:從點擊到滑動的開發全流程

摘要 隨著 HarmonyOS NEXT 的不斷發展&#xff0c;ArkUI 逐漸成為主流的 UI 構建方式。而用戶交互在任何應用中都是基礎而又關鍵的一環&#xff0c;如何利用 ArkUI 提供的觸摸事件機制&#xff0c;如 onTouch、onClick、onSwipe 等&#xff0c;來實現自然、順滑、用戶友好的交互…

Tailwind CSS 自定義工具類與主題配置指南

一、自定義工具類配置在 src/tailwind.css 文件中&#xff0c;我們可以通過 layer utilities 指令添加自定義工具類&#xff1a;tailwind base; tailwind components; tailwind utilities;layer utilities {/* 自定義工具 上下浮動效果 */.animate-floatY {animation: floatY 3…

【代碼隨想錄二刷|704.二分查找、27.移除元素、977.有序數組的平方】

704.二分查找 題目鏈接&#xff1a;704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int search(vector<int>& nums, int target) {//不用二分查找&#xff0c;直接求// for(int i0;i<nums.size();i){// if(nums[i]target)…

基于Vue的工業設備大屏可視化模板(含設備地圖分布+宣傳模塊+報表展示+三維模型加載預覽)

場景 為實現工業設備可視化大屏需求&#xff0c;可實現基于地圖的設備數據管理&#xff0c;點擊具體設備可進行詳細介紹和三維模型展示。 可播放宣傳視頻&#xff0c;可展示PM數據報表等數據&#xff0c;可接受報警數據提醒、統計等數據。 基于現有開源平臺框架進行二次改造…

堆(Heap)優先級隊列(Priority Queue)

一、堆的概念堆&#xff08;Heap&#xff09;是一種特殊的基于樹的數據結構&#xff0c;通常分為最大堆和最小堆兩種類型。它滿足堆屬性&#xff1a;對于最大堆&#xff0c;父節點的值總是大于或等于其子節點的值&#xff1b;而對于最小堆&#xff0c;父節點的值總是小于或等于…

踩坑記錄:因版本不匹配導致 Boost 1.85 編譯失敗的完整解決過程

踩坑記錄&#xff1a;因版本不匹配導致 Boost 1.85 編譯失敗的完整解決過程 轉載請注明出處&#xff0c;歡迎評論區交流。 背景 最近在 Windows 11 VS2022 環境下嘗試用 b2 編譯 Boost 1.85.0&#xff0c;結果一路踩坑&#xff0c;最后發現罪魁禍首是 Boost.Build 自帶的 msv…

InfluxDB Line Protocol 協議深度剖析(二)

四、Line Protocol 寫入操作與實踐&#xff08;一&#xff09;HTTP API 寫入使用 HTTP API 是通過 Line Protocol 寫入數據到 InfluxDB 的常用方式。InfluxDB 1.x&#xff1a;請求方式為 POST&#xff0c;URL 格式為 “http://host:port/write?dbdatabase_name”。其中&#x…

負載均衡:提升業務性能的關鍵技術

一.負載均衡&#xff08;3.6 &#xff09;1.1.什么是負載均衡&#xff1a;負載均衡&#xff1a;Load Balance&#xff0c;簡稱LB&#xff0c;是一種服務或基于硬件設備等實現的高可用反向代理技術&#xff0c;負載均 衡將特定的業務(web服務、網絡流量等)分擔給指定的一個或多個…

【STM32項目】智能家居(版本1)

????大家好&#xff0c;這里是5132單片機畢設設計項目分享&#xff0c;今天給大家分享的是基于《基于STM32的智能家居設計》。 目錄 1、系統功能 2.1、硬件清單 2.2、功能介紹 2.3、控制模式 2、演示視頻和實物 3、系統設計框圖 4、軟件設計流程圖 5、原理圖 6、主…

OpenSCA開源社區每日安全漏洞及投毒情報資訊—2025年7月24日

2025年7月24日安全風險情報資訊在野漏洞風險&#xff08;CVE未收錄&#xff09;&#xff1a;1公開漏洞精選&#xff1a;2組件投毒情報&#xff1a;2在野漏洞風險&#xff08;CVE未收錄&#xff09;1.1 gemini-cli項目潛在命令注入漏洞項目詳情項目描述&#xff1a;gemini-cli是…

飛算 JavaAI 深度實戰:從老項目重構到全棧開發的降本增效密碼

飛算 JavaAI 深度實戰&#xff1a;從老項目重構到全棧開發的降本增效密碼引言正文一、智能引導模塊&#xff1a;老項目重構的 “手術刀” 級解決方案1.1 本地化智能分析&#xff1a;IDEA 插件實操演示1.1.1 &#x1f4cc; IDEA 插件安裝步驟1.1.1.1 首先打開idea工具&#xff0…

分布式推客系統開發全解:微服務拆分、傭金結算與風控設計

一、推客系統概述與市場背景推客系統&#xff08;也稱為分銷系統或社交電商系統&#xff09;已成為現代電商平臺和內容平臺的重要增長引擎。根據最新統計數據&#xff0c;2023年社交電商市場規模已突破3萬億元&#xff0c;占整體電商市場份額的25%以上。推客系統的核心價值在于…