真香定律!我用這種模式重構了第三方登錄

分享是最有效的學習方式。

博客:https://blog.ktdaddy.com/

老貓的設計模式專欄已經偷偷發車了。不甘愿做crud boy?看了好幾遍的設計模式還記不住?那就不要刻意記了,跟上老貓的步伐,在一個個有趣的職場故事中領悟設計模式的精髓吧。還等什么?趕緊上車吧。

故事

辦公室里,小貓托著腮幫對著電腦陷入了思考。就在剛剛,他接到了領導指派的一個任務,業務調整,登錄方式要進行拓展。例如需要接入第三方的微信登錄,企業微信授權登錄等等。

原因大概是這樣,現在大環境不好,原來面向B端企業員工的電商業務并不好做,新客拓展比較困難,業務想要有更好的起色著實比較困難,所以決策層決定要把登錄的口子放開,原來支持手機密碼登錄以及手機驗證碼進行登錄,現在為了更好地推廣,需要支持微信掃碼關注企業公眾號后登錄,企業微信,微博等等一些列的第三方登錄模式。

說白了未來到底會有多少種登錄方式不得而知,那么面對這樣一個棘手的問題,小貓又該何去何從?

概述

登錄問題相信后端小伙伴都有接觸過,最簡單的可能就是做一個權限系統就會用到登錄名+密碼+驗證碼進行登錄,繼而稍微復雜一些可能會涉及手機驗證碼登錄。現在隨著第三方平臺的層出不窮,我們很多網站其實都提供了聯合登錄。用戶掏出手機簡單地一個掃碼動作即可完成初步的注冊登錄功能。這種方式一定程度上能夠給當前的網站帶來更多的流量。

關于小貓遇到的問題,咱們嘗試從下面幾個點去解決。

在這里插入圖片描述

登錄演化

聊到登錄,我們首先去了解一下整個登錄認證的發展階段,以及目前比較常見也相對比較復雜的微信公眾號授權登錄流程。

基于Cookie/Session進行驗證登錄

在早期,也就是可能是單體系統的時代,亦或者站在java開發者角度來說是jsp時代的時候,我們用的登錄方式就是Cookie/Session驗證的方式。關于Cookie以及Session相信很多后端的小伙伴都應該知道,當然若真有不清楚的,大家可以自己查閱一下相關資料。
利用這種方式登錄的流程其實還是比較簡單的,如下流程:

在這里插入圖片描述

基于上述登錄成功后,服務端將用戶的身份信息存儲在Session里,并將session ID通過cookie傳遞給客戶端。后續的數據請求都會帶上cookie,服
務端根據cookie中攜帶的session id來得到辨別用戶身份。

簡單的java偽代碼如下:

...
session.setAtrrbuite("user",user);
...
session.getAttrbuite("user");

當然上述的偽代碼還是基于最最原始的寫法去寫的,關于這種登錄的框架,其實目前市面上也有比較成熟的,例如輕量級的shiro,spring本身自帶的權限認證框架也有。

隨著業務的發展,系統訪問量級的增大,我們漸漸發現這種方式存在著一些問題:

  1. 由于服務端需要對接大量的客戶端,也就需要存放大量的Seesion ID,這樣就會導致服務器壓力過大。如果服務器是個集群,為了同步登錄的狀態,需要將Session ID同步到每一臺服務器上,無形中增加了服務器端的維護成本。
  2. 由于Session ID存放在Cookie中,所以無法避免CSRF攻擊(跨站請求偽造)。

當然其他問題也歡迎小伙伴們進行補充。
為了解決這一些列的問題,我們漸漸演化出了另外一種登錄認證方式————基于token進行認證登錄。

基于TOKEN進行認證登錄

現在的系統大部分都是前后端分離開發的。后端大多使用了WEB API,此時token無疑是處理認證的最好方式。

Session 方案中用戶信息(以Session記錄形式)存儲在服務端。而Token方案中(以Token形式)存儲在客戶端,服務端僅驗證Token合法性即可。基于Token的身份驗證是無狀態的,不將用戶信息存在服務器中。這種概念解決了在服務端存儲信息時的許多問題。NoSession意味著咱們的程序可以根據需要去增減機器,而不用去擔心用戶是否登錄。

咱們一起來看一下如果使用TOKEN整個流程。

在這里插入圖片描述

關于上述token機制的特點有以下幾點:

  • 無狀態、可擴展:在客戶端存儲的Token是無狀態的,并且能夠被擴展。基于這種無狀態的和不存儲Session信息,所以不會對服務器端造成壓力,負載均衡器能夠將用戶信息從一個服務器傳到其他服務器上,即使是服務器集群,也不需要增加維護成本。

  • 可擴展性:Tokens能夠創建與其它程序共享權限的程序。(即,我們所說的第三方平臺聯合登錄的時候,token的生成機制以及驗證可以由第三方系統進行聯合驗證登錄)

  • 安全性:請求中發送Token而不是發送Cookie,能夠防止CSRF(跨站請求偽造)。即客戶端使用Cookie存儲了Tooken,Cookie也僅僅是一個存儲機制而不是用于認證。不將信息存儲在Session中,讓我們少了對Session的操作。Token也可以存放在前端任何地方,可以不用保存在Cookie中,提升了頁面的安全性。Token是會失效的,一段時間之后用戶需要重新驗證。

  • 多平臺跨域:對應用程序和服務進行擴展的時候,需要介入各種各種的設備和應用程序。只要用戶有一個通過了驗證的token,數據和資源就能夠在任何域上被請求到。

微信掃碼跳轉公眾號認證登錄

這也是后續小貓遇到的問題,以及需要和其他第三方Api主要對接的。其實關于掃碼認證登錄也是基于token機制的一種拓展。只不過第三方的平臺在token機制上新增了獲取二維碼進行二次確認的過程。咱們以微信掃碼跳轉公眾號登錄為例來看一下整個流程。其他的第三方登錄流程其實也是大同小異,咱們了解一個流程即可,不同的平臺只是對接不同的api而已。流程圖如下:

在這里插入圖片描述

從上面這幅圖看到,掃碼登錄其實復雜就復雜在獲取token這個步驟上,當獲取完畢token之后,其后續的業務邏輯其實基本也是一樣的。

其實其他第三方的登錄其實也是大同小異,最主要的難點是在如何獲取token上,我們只要認真看完對接的api,其實問題也基本都能迎刃而解。

說明一下,老貓這里繪圖用了drawio工具,如果想要知道老貓的繪圖思路,大家可以看看這里《繪圖思路》

如何兼容多套?

看完上述之后,相信大家會對認證登錄心里有桿秤了。細節方面其實只要去查詢相關平臺的api,然后去擼代碼就好了。但是實現一套倒是還好,但是現在小貓遇到的問題是需要在原邏輯上去豐富登錄的代碼。如果在老的代碼上通過if else的方式去實現多套登錄邏輯,那估計后面又是屎山。

這里,其實我們可以引入“適配器設計模式”去解決這樣的問題。

什么是適配器模式?

適配器模式(英文名:Adapter Pattern)是指將一個類的接口轉換成用戶期望的另一個接口,使得原本接口不兼容的類可以一起工作。

適配器模式可以分為兩類:對象適配器模式和類適配器模式。對象適配器模式通過組合實現適配,而類適配器模式則通過繼承實現適配。

此外,還有一種特殊的適配器模式——缺省適配器模式它由一個抽象類實現,并在其中實現目標接口中所規定的所有方法,但這些方法的實現通常是空方法,由具體的子類來實現具體的功能。適配器模式的應用可以提高代碼的復用性和可維護性,同時幫助解決不同接口之間的兼容性問題。

上面的概念比較抽象,其實在咱們的日常生活中也有這樣的例子,例如手機充電轉換頭,顯示器轉接頭等等。

適配器模式重構第三方登錄

話不多說,直接開干,我們就針對小貓的遇到這個第三方登錄的場景,咱們用代碼重構一把。(當然,這里我們側重的還是偽代碼)。跟著老貓,咱們一步步走好代碼的演化。

咱們先看一下老的業務代碼,如下:

public class UserLoginService {public ApiResponse<String> regist(String userName,String password) {//...dosomethingreturn ApiResponse.success("success");}public ApiResponse login(String userName, String password) {return null;}
}

接下來由于小貓的業務會發生變更,新的登錄方式會層出不窮,所以,我們得遵循之前提到的軟件設計原則去更好地寫一下業務代碼。我們遵循之前提到的開閉原則,于是我們邁出了重構代碼的第一步,我們將創建一個新的第三方登錄的類來專門處理第三方的登錄對接。如下:

public class ThirdPartyUserLoginService extends UserLoginService {public ApiResponse loginForQQ(String openId) {/*** openid 全局唯一,咱們直接作為用戶名* 默認密碼QQ_EMPTY* 注冊(原來父類中有注冊實現)* 調用原來的登錄*/return loginForRegist(openId, null);}public ApiResponse loginForWechat(String openId) {return null;}public ApiResponse loginForToken(String token) {return null;}public ApiResponse loginForTel(String tel, String code) {return null;}public ApiResponse<String> loginForRegist(String userName, String password) {super.login(userName, password);return super.login(userName, password);}
}

寫到這里,其實咱們已經集成了多種登錄方式的代碼兼容,但是這種實現方式顯然是不太優雅的,看起來比較死板,在登錄的時候我們甚至還得去判斷客戶到底是用什么去做登錄的,然后去分別調用不同第三方平臺的認證方式。

我們接下來演化開始用適配器。如下代碼:
首先我們定義出一個標準的適配接口:

public interface LoginAdapter {boolean support(Object adapter);ApiResponse login(String id,Object adapter);
}

根據上面我們看到,我們有QQ方式登錄,有微信方式登錄,有電話驗證碼方式登錄。所以我們對應的就應該有相關的這些方式的適配器的實現。由于代碼重復,所以在此老貓就寫QQ和微信這兩種偽代碼,其他的暫時先偷個懶。

/*** @author 公眾號:程序員老貓* @date 2024/3/3 22:47*/
public class LoginForQQAdapter implements LoginAdapter {@Overridepublic boolean support(Object adapter) {return adapter instanceof LoginForQQAdapter;}@Overridepublic ApiResponse login(String id, Object adapter) {return null;}
}public class LoginForWeChatAdapter implements LoginAdapter {@Overridepublic boolean support(Object adapter) {return adapter instanceof LoginForWeChatAdapter;}@Overridepublic ApiResponse login(String id, Object adapter) {return null;}
}

有了這些適配器之后,我們就統一對外給出去接口:


public interface IPassportForThird {ApiResponse loginForQQ(String openId);ApiResponse loginForWechat(String openId);ApiResponse<String> loginForRegist(String userName, String password);
}

最后創建統一適配器。

@Slf4j
public class PassportForThirdAdapter extends UserLoginService implements IPassportForThird{@Overridepublic ApiResponse loginForQQ(String openId) {return doLogin(openId,LoginForQQAdapter.class);}@Overridepublic ApiResponse loginForWechat(String openId) {return doLogin(openId,LoginForWeChatAdapter.class);}@Overridepublic ApiResponse<String> loginForRegist(String userName, String password) {super.login(userName, password);return super.login(userName, password);}//用到簡單工廠模式以及策略模式private ApiResponse doLogin(String openId,Class<? extends LoginAdapter> clazz) {try {LoginAdapter adapter = clazz.newInstance();if(adapter.support(adapter)){return adapter.login(openId,adapter);}}catch (Exception e) {log.error("exception is",e);}return null;}
}

最終我們看一下實現的類圖:

在這里插入圖片描述

上述我們就用了適配器的模式簡單重構了現有的第三方登錄的代碼,當然上述可能還存在一些代碼的缺陷,大家也不要太過較真,在此給大家在日常開發中多點思路。

大家可能會對每個適配器的support()方法有點疑問,用來決斷兼容。這里support()方法的參數也是Object類型的,而support()方法來自接口。適配器的實現并不依賴接口,其實我們也可以直接將LoginAdapter移除。

在上述重構的例子中,其實咱們不僅僅用到了適配器模式,其實還用到了簡單工廠模式的特性。

總結

其實在我們日常的開發中,適配器模式是比較常用的一種設計模式,不僅僅使用上述場景,其實在很多其他api的對接的場景也有適用。例如,在電商業務場景中會涉及到各種對接,說到買賣就會牽扯到供應商的對接,第三方分銷渠道客戶的對接,其中必然涉及模型不一致需要適配轉換的場景,比如供應商商品信息和標準商城商品信息等等。當然老貓在此也只是做了一下簡單羅列。希望大家在后面的工作中可以參考用到。

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

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

相關文章

improve-echarts餅圖自適應(分辨率放大縮小)

1.echarts 本身存在放大縮小圖表不變的情況&#xff0c;要求要圖表適應分辨率&#xff0c;根據分辨率放大縮小來進行適應與響應式。 餅圖 <!-- 餅狀 --><div class"leftrcyle"><div class"ciclye"><div id"cicly" class&q…

2023人機交互期末復習

考試題型及分值分布 1、選擇題&#xff08;10題、20分&#xff09; 2、填空題&#xff08;10題、20分&#xff09; 3、判斷題&#xff08;可選、5題、10分&#xff09; 4、解答題&#xff08;5~6題、30分&#xff09; 5、分析計算題&#xff08;1~2題、20分&#xff09; 注意&…

PHP+MySQL實現后臺管理系統增刪改查之夠用就好

說明 最近要給博客弄個后臺&#xff0c;不想搞得很復雜&#xff0c;有基本的增刪改查就夠了&#xff0c;到網上找了一圈發現這個不錯&#xff0c;很實用&#xff0c;希望可以幫到大家&#xff0c;需要的朋友評論區留下郵箱&#xff0c;我安排發送。 演示效果 項目介紹 本項目…

Jetty使用入門

Jetty使用入門 社區當前推薦開發者使用Jetty 12.X版本。 依據End of Community Support for Jetty 9.x - June 2022&#xff0c;社區對Jetty 9.x的支持&#xff0c;已在2022年6月1日停止。 依據End of Community Support for Jetty 10 / Jetty 11 - January 2024&#xff0c;…

帶使能控制的鋰電池充放電解決方案

一、產品概述 TP4594R 是一款集成線性充電管理、同步升壓轉換、電池電量指示和多種保護功能的單芯片電源管理 SOC&#xff0c;為鋰電池的充放電提供完整的單芯片電源解決方案。 TP4594R 內部集成了線性充電管理模塊、同步升壓放電管理模塊、電量檢測與 LED 指示模塊、保護模塊…

關于python函數參數傳遞

參數傳遞 在 python 中&#xff0c;類型屬于對象&#xff0c;對象有不同類型的區分&#xff0c;變量是沒有類型的&#xff1a; 在下面的代碼示例重&#xff0c;[1,2,3] 是 List 類型&#xff0c;“qayrup” 是 String 類型&#xff0c;而變量 a 是沒有類型&#xff0c;它僅僅…

#WEB前端

1.實驗&#xff1a;vscode安裝&#xff0c;及HTML常用文本標簽 2.IDE&#xff1a;VSCODE 3.記錄&#xff1a; &#xff08;1&#xff09;網頁直接搜索安裝vscode &#xff08;2&#xff09;打開vscode&#xff0c;在下圖分別安裝以下插件&#xff1a; Html Css Support …

C++11線程同步之互斥鎖

C11線程同步之互斥鎖 std::mutex成員函數線程同步 std::lock_guardstd::recursive_mutexstd::timed_mutex 進行多線程編程&#xff0c;如果多個線程需要對同一塊內存進行操作&#xff0c;比如&#xff1a;同時讀、同時寫、同時讀寫對于后兩種情況來說&#xff0c;如果不做任何的…

《互聯網的世界》第四講-擁塞控制與編碼

需要澄清的一個誤區是&#xff0c;擁塞絕不是發送的數據量太大導致&#xff0c;而是數據在極短的時間段內到達了同一個地方以至于超過了網絡處理容量導致&#xff0c;擁塞的成因一定要考慮時間因素。換句話說&#xff0c;擁塞由大突發導致。 只要 pacing&#xff0c;再多的數據…

2024.3.4訓練記錄(8)

文章目錄 CF 459D Pashmak and Parmidas problemCF 1388C Uncle Bogdan and Country HappinessCF 1525D ArmchairsCF 220B Little Elephant and Array CF 459D Pashmak and Parmida’s problem 題目鏈接 最近感覺對數據結構題的反應度提升了&#xff0c;這一題是上午看的但是…

動態規劃(算法競賽、藍橋杯)--樹形DP樹形背包

1、B站視頻鏈接&#xff1a;E18 樹形DP 樹形背包_嗶哩嗶哩_bilibili #include <bits/stdc.h> using namespace std; const int N110; int n,V,p,root; int v[N],w[N]; int h[N],to[N],ne[N],tot; //鄰接表 int f[N][N];void add(int a,int b){to[tot]b;ne[tot]h[a];h[a…

數倉項目6.0(一)

尚硅谷大數據項目【電商數倉6.0】企業數據倉庫項目_bilibili 數據流轉過程 用戶??業務服務器??數據庫存儲??數倉統計分析??數據可視化 數據倉庫處理流程&#xff1a;數據源??加工數據??統計篩選數據??分析數據 數據庫不是為了數據倉庫服務的&#xff0c;需要…

B084-SpringCloud-Zuul Config

目錄 zuul系統架構和zuul的作用zuul網關實現配置映射路徑過濾器 Config概述云端管理本地配置 zuul zuul是分布式和集群后前端統一訪問入口 系統架構和zuul的作用 zuul把自己注冊進eureka&#xff0c;然后可通過前端傳來的服務名發現和訪問對應的服務集群 為了預防zuul單點故…

Java 枚舉類的深入理解與應用

Java 的枚舉類是一種特殊的類&#xff0c;通常表示一組常量。在編譯或設計時&#xff0c;當我們知道所有變量的可能性時&#xff0c;盡量使用枚舉類型。本文將通過一個具體的例子&#xff0c;深入探討 Java 枚舉類的定義、使用和高級特性。 目錄 枚舉類的定義與使用枚舉類的構造…

【OJ】求和與計算日期

文章目錄 1. 前言2. JZ64 求123...n2.1 題目分析2.2 代碼 3. HJ73 計算日期到天數轉換3.1 題目分析3.2 代碼 4. KY222 打印日期4.1 題目分析4.2 代碼 1. 前言 下面兩個題目均來自牛客&#xff0c;使用的編程語言是c&#xff0c;分享個人的一些思路和代碼。 2. JZ64 求123…n …

Vue 賦值后原數據隨賦值后的數據的變化而變化

很常見的&#xff0c;當我們直接用“”號等方式直接賦值后 原數據會隨賦值后的數據的變化而變化 但是有時候我們的需求是不需要原數據跟隨變化 所以怎么解決呢&#xff1f; 解決辦法有&#xff1a; 1.使用Object.assign() 方法 2.使用深拷貝函數 JSON.parse() 3.使用第三方庫lo…

畢業生信息招聘平臺|基于springboot+ Mysql+Java的畢業生信息招聘平臺設計與實現(源碼+數據庫+文檔+PPT)

目錄 論文參考 摘 要 數據庫設計 系統詳細設計 文末獲取源碼聯系 論文參考 摘 要 隨著社會的發展&#xff0c;社會的各行各業都在利用信息化時代的優勢。計算機的優勢和普及使得各種信息系統的開發成為必需。 畢業生信息招聘平臺&#xff0c;主要的模塊包括查看管理員&a…

#ifndef 和 #pragma once的區別

#ifndef 和 #pragma once 都是用來防止頭文件被重復包含的&#xff0c;但它們的工作方式和兼容性有所不同&#xff1a; #ifndef 是 C 的標準語法&#xff0c;它依賴于不重復的宏名稱&#xff0c;保證了包含在 #endif 的內容不會被重復包含。這個內容可以是一個文件的所有內容&…

Webpack配置與運行基礎教程

在前端開發中&#xff0c;Webpack是一款非常流行的模塊打包工具&#xff0c;它可以幫助我們將多個文件打包成一個或多個靜態資源文件&#xff0c;從而提高前端項目的性能和可維護性。本文將為你介紹Webpack的基礎配置和運行方法&#xff0c;幫助你快速上手Webpack。 什么是Web…

基于Springboot的無人智慧超市管理系統(有報告)。Javaee項目,springboot項目。

演示視頻&#xff1a; 基于Springboot的無人智慧超市管理系統&#xff08;有報告&#xff09;。Javaee項目&#xff0c;springboot項目。 項目介紹&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三層體系…