使用 Vue 3 插件(Plugin)實現 OIDC 登錄和修改密碼(OIDC 系統以 Keycloak 為例)

来源:https://www.cnblogs.com/aobaxu/archive/2023/11/05/17810124.html
-Advertisement-
Play Games

背景 目前單位系統常用 Keycloak 作為認證系統後端,而前端之前寫的也比較隨意,這次用 Vue 3 插件以及 Ref 響應式來編寫這個模塊。另外,這個可能是全網唯一使用 keycloak 的 OIDC 原生更新密碼流的介紹代碼。 設計 依賴庫選擇 OIDC 客戶端,這裡選擇 oidc-clie ...


背景

目前單位系統常用 Keycloak 作為認證系統後端,而前端之前寫的也比較隨意,這次用 Vue 3 插件以及 Ref 響應式來編寫這個模塊。另外,這個可能是全網唯一使用 keycloak 的 OIDC 原生更新密碼流的介紹代碼。

設計

依賴庫選擇

OIDC 客戶端,這裡選擇 oidc-client-ts 來提供 OIDC 相關的服務,根據目前的調研這個算是功能比較齊全、相容性比較好的 OIDC 客戶端了。像 keycloak.js,其實也沒有修改密碼和自動刷新 token 的功能。另外像 Auth0 Vue SDK 則只能用於 Auth0,但他設計上還是不錯的,也是通過 Vue 3 原生的插件功能實現的。

具體設計

根據 Vue 3 的官方插件文檔,主要需要兩部分組成,一個是需要定義一個 Plugin 併在裡面使用 provide 來提供對象,另一個則是需要定義一個方法使用 inject 來接收提供的對象。

這裡給原本的 oidc-client-ts 里的 UserManager 來個套娃,外層這個套一層,叫 AuthManager 。這樣就可以將一些初始化時載入 LocalStorage 里的 token 等等邏輯封裝在這裡面,同時也可以對外暴露一些 Ref 讓其他組件可以監聽變化。

代碼

廢話不多說了,咱還是老樣子,直接上代碼

auth-manager.ts

import { UserManager, UserManagerSettings } from 'oidc-client-ts';
import { Plugin, inject, ref } from 'vue';

/**
 * 用於註入的 key
 */
const PROVIDE_KEY = Symbol('oidc-provider');
/**
 * 用戶信息
 */
interface UserInfo {
  /**
   * 用戶 id
   */
  userId: string;
  /**
   * 用戶名
   */
  username: string;
  /**
   * token
   */
  token: string;
  /**
   * 姓
   */
  lastName: string;
  /**
   * 名
   */
  firstName: string;
  /**
   * 郵箱
   */
  email: string;
  /**
   * 認證時間
   */
  authTime: number;
  /**
   * 角色
   */
  roles: Array<string>;
}
/**
 * 認證管理器
 */
class AuthManager {
  /**
   * token
   */
  accessToken = ref('');
  /**
   * 用戶信息
   */
  userInfo = ref<UserInfo>();
  /**
   * oidc 客戶端
   */
  private oidc: UserManager;
  /**
   * 構造函數
   * @param settings oidc 客戶端配置
   */
  constructor(settings: UserManagerSettings) {
    this.oidc = new UserManager(settings);
    // 當用戶登錄時,更新 token 和用戶信息
    this.oidc.events.addUserLoaded((user) => {
      this.accessToken.value = user.access_token;
      this.userInfo.value = {
        userId: user.profile.sub,
        username: user.profile.preferred_username || '',
        token: user.access_token,
        lastName: '',
        firstName: '',
        email: user.profile.email || '',
        authTime: user.profile.auth_time || +new Date(),
        roles: (user.profile.roles as Array<string>) || [],
      };
      // 開啟靜默刷新,清除過期狀態
      this.oidc.startSilentRenew();
      this.oidc.clearStaleState();
    });
    // 當更新 token 失敗時,退出登錄
    this.oidc.events.addSilentRenewError(() => {
      this.logout();
    });
    // 當 token 過期時,退出登錄
    this.oidc.events.addAccessTokenExpired(() => {
      this.logout();
    });
    // 初始化時載入用戶信息
    this.loadUser();
  }
  /**
   * 載入用戶信息
   */
  async loadUser() {
    const user = await this.oidc.getUser();
    // 如果能載入出來則將信息放到 Ref 里
    if (user) {
      this.accessToken.value = user.access_token;
      this.userInfo.value = {
        userId: user.profile.sub,
        username: user.profile.preferred_username || '',
        token: user.access_token,
        lastName: '',
        firstName: '',
        email: user.profile.email || '',
        authTime: user.profile.auth_time || +new Date(),
        roles: (user.profile.roles as Array<string>) || [],
      };
      this.oidc.startSilentRenew();
      this.oidc.clearStaleState();
    }
  }
  /**
   * 登錄
   */
  login() {
    return this.oidc.signinRedirect();
  }
  /**
   * 檢查是否已登錄
   * @returns 是否已登錄
   */
  async checkLogin(): Promise<boolean> {
    const user = await this.oidc.getUser();
    return user != null && !user.expired;
  }
  /**
   * 退出登錄
   */
  logout() {
    this.oidc.stopSilentRenew();
    this.accessToken.value = '';
    this.userInfo.value = undefined;
    return this.oidc.signoutRedirect();
  }
  /**
   * 刷新 token
   * @param force 是否強制刷新
   */
  async refresh(force?: boolean) {
    // 如果不是強制刷新,則先檢查用戶可用,如果用戶可用則不刷新
    if (!force) {
      const user = await this.oidc.getUser();
      if (user != null && !user.expired) {
        return user;
      }
    }
    return this.oidc.signinSilent();
  }
  /**
   * 登錄回調
   */
  loginCallback() {
    return this.oidc.signinCallback();
  }
  /**
   * 重置密碼
   */
  resetPassword() {
    // 這裡使用 keycloak 登錄流中的更新密碼流實現
    this.oidc.signinRedirect({
      scope: 'openid',
      extraQueryParams: {
        // 這裡設置額外參數時,帶上 keycloak 的更新密碼流
        kc_action: 'UPDATE_PASSWORD',
      },
    });
  }
}

/**
 * 認證插件
 */
const authPlugin: Plugin<UserManagerSettings> = {
  install: (app, options) => {
    const auth = new AuthManager(options);
    app.provide(PROVIDE_KEY, auth);
  },
};

/**
 * 使用認證管理器
 * @returns 認證管理器
 */
const useAuthManager = () => {
  return inject<AuthManager>(PROVIDE_KEY);
};

export { authPlugin, useAuthManager };


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近重構並精簡了Dapper.Lite,然後把不依賴Dapper的版本LiteSql也重構了一下,和Dapper.Lite保持一致。感覺這兩款ORM基本完工,自薦一下。 .NET的ORM雖多,堪用的不多,何為堪用,EF是官方的,質量高,堪用。Dapper用戶量大,現在BUG基本改的差不多了,也基本不 ...
  • MongoDB+SignalR+Hangfire+Vue2+百度地圖實現GPS實時定位 一、實現效果 二、安裝MongoDB 可以自行參考菜鳥鏈接:MongoDB 教程 | 菜鳥教程 (runoob.com) 1.下載mongodb資料庫安裝包: 網盤鏈接:https://pan.baidu.com ...
  • 本文將分享其在 Windows/docker 中的使用,使用 nssm 部署成服務的方案腳本,區域網中自定義功能變數名稱解決https提示不安全的解決方案,以及一路踩過的坑。 ...
  • 前言 本篇文章主要介紹的關於本人在使用Linux記錄筆記的一些使用方法和經驗,溫馨提示,本文有點長,約1.7w字,幾十張圖片,建議收藏查看。 一、Linux基礎使用 1,伺服器查看及時日誌 tail -500f catalina.out 2,如何退出logs日誌 ctrl+c 或kill -9 pi ...
  • 在使用selenium的時候,通常要部署到伺服器上,這時通常要連接遠程的瀏覽器驅動,並且還可以在任意一臺電腦看到遠程瀏覽器界面,這時就要部署遠程瀏覽器驅動服務與VNC,以下是步驟: 遠程機器下載瀏覽器和對應的驅動 下載selenium-server-standalone.jar,下載地址:selen ...
  • 哈嘍大家好,我是鹹魚 在文章《三劍客之 sed》中鹹魚向大家介紹了文本三劍客中的 sed sed 全名叫 stream editor,流編輯器,用程式的方式來編輯文本 那麼今天鹹魚打算講一下我在用 sed 原地替換文件時遇到的趣事 sed 讓文件屬性變了? 有這麼一個普通文件 test.txt ,內 ...
  • 1,用戶 和 許可權 的基本概念 1.1 ls 擴展 ls -l 1.2 chmod 簡單使用(重要) + 是加許可權, - 是減許可權 chmod 修改文件許可權 chmod 修改目錄許可權: 想要在目錄下執行終端命令,就必須要有可執行許可權。 1.3 超級用戶 2,組管理終端命令 groupadd 組名 : ...
  • 這裡簡單介紹一下如何處理解決Linux平臺下Oracle 19c啟動時,告警日誌出現ORA-00800錯誤的問題,詳情介紹請見下麵內容: 環境描述: 操作系統:Red Hat Enterprise Linux release 8.8 (Ootpa) 資料庫 :19.16.0.0.0 企業版 問題描述 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...