開發者技術前線 ,匯集技術前線快訊和關注行業趨勢,大廠干貨,是開發者經歷和成長的優秀指南。
更新時間:2024-05-10 16:16:07作者:佚名
Web 應用程序使用 / 和 http 作為通信協議。 HTTP 是一種無狀態協議。 來自瀏覽器的每個請求都將由服務器獨立處理,并且不會與之前或后續的請求關聯。 這個過程如下圖所示。 三個請求/響應對之間沒有任何聯系。
但這也意味著任何用戶都可以通過瀏覽器訪問服務器資源。 如果要保護服務器的某些資源,就必須限制瀏覽器請求; 限制瀏覽器請求,必須識別瀏覽器請求,響應合法請求,忽略非法請求; 為了識別瀏覽器請求,必須知道瀏覽器請求狀態。 既然http協議是無狀態的,那就讓服務器和瀏覽器共同維護一個狀態吧!這就是會話機制
2. 會話機制
瀏覽器第一次請求服務器時,服務器會創建一個會話并將會話 ID 作為響應的一部分發送到瀏覽器。 瀏覽器存儲會話 ID 并在后續的第二個和第三個請求中攜帶它。 服務器通過獲取請求中的 ID來判斷是否是同一個用戶。 這個過程如下圖所示。 后續請求與第一個請求相關。
服務器將會話對象保存在內存中。 瀏覽器如何保存 id? 你可能會想到兩種方法:
1、請求參數;
2、.
使用 id作為每個請求的參數,服務器在收到請求時自然可以解析參數得到 id,并以此來判斷是否來自同一個。 顯然,這種方法并不可靠。 然后讓瀏覽器自己維護 id。 每次發送 http 請求時,瀏覽器都會自動發送會話 ID。 該機制就是用來做到這一點的。它是瀏覽器用來存儲少量數據的機制。 數據以“鍵/值”的形式存儲。 瀏覽器在發送http請求時自動附加信息。
當然,機制也已經實現了。 訪問服務器時,可以在瀏覽器中看到一個名稱“”。 這是會話機制維護的會話ID。 使用的請求響應流程如下:
3. 登錄狀態
通過會話機制,登錄狀態一目了然。 我們假設瀏覽器第一次請求服務器時,需要輸入用戶名和密碼來驗證身份。 服務器獲取用戶名和密碼并在數據庫中進行比較。 如果正確,則表示當前正在舉行會議。 用戶是合法用戶,這個應該被標記為“已授權”或者“已登錄”等,既然是的狀態,自然需要保存在對象中。 在對象中設置登錄狀態如下
HttpSession?session?=?request.getSession();
session.setAttribute("isLogin",?true);
當用戶再次訪問時,在對象中查看登錄狀態
HttpSession?session?=?request.getSession();
session.getAttribute("isLogin");
實現登錄狀態的瀏覽器請求服務器模型如下圖所示
登錄機制是通過每次請求受保護資源時檢查對象中的登錄狀態來實現的,只有=true的才能被訪問。
02多系統的復雜性
Web系統已經從古代的單一系統發展到由多個系統組成的應用程序群。 面對如此多的系統,用戶是否必須一一登錄,然后一一退出?如下圖所示
Web系統已經從單一的系統發展到由多個系統組成的應用群。 復雜性應該由系統本身來承擔,而不是由用戶來承擔。Web系統無論內部多么復雜,對于用戶來說都是一個統一的整體。 也就是說,用戶訪問Web系統的整個應用組與訪問單個系統是一樣的。 只需一次登錄/注銷就足夠了。
單系統登錄方案雖然很完美,但已經不再適合多系統應用群體。 為什么?
單系統登錄方案的核心是攜帶 ID來維持瀏覽器和服務器之間的會話狀態。但也有限制。 這個限制就是域名(通常對應網站的域名)。 瀏覽器發送http請求時,會自動攜帶與匹配的域名,并非全部
既然如此,何不將Web應用組中所有子系統的域名統一到一個頂級域名下verify什么意思,比如“*.”,然后將它們的域名設置為“”。 這種方式理論上是可行的,甚至早期很多多系統登錄都采用這種共享同一個域名的方式。
然而,可行并不意味著好,共享方式也有很多局限性。 首先,應用組的域名必須統一; 其次,應用組中各個系統使用的技術(至少是Web服務器)必須相同,否則鍵值不同,會話無法維持,共享方式無法實現跨域-語言技術平臺登錄。 ,例如java、php、.net系統之間; 第三,本質上是不安全的。
因此,我們需要一種新的登錄方式來實現多系統應用組的登錄,這就是單點登錄
03單點登錄
什么是單點登錄?單點登錄的全稱是Sign On(以下簡稱SSO)。 意味著登錄多系統應用組中的一個系統后,無需再次登錄即可在所有其他系統中獲得授權。 它包括單點登錄和單點注銷。
1. 登錄
與單系統登錄相比,SSO需要獨立的認證中心。 只有認證中心才能接受用戶的用戶名、密碼等安全信息。 其他系統不提供登錄入口,僅接受認證中心的間接授權。 間接授權是通過token實現的。 SSO認證中心驗證用戶的用戶名和密碼是否正確,并創建授權令牌。 在接下來的跳轉過程中,將授權token作為參數發送給各個子系統,子系統獲取到token。 ,即您有權創建部分會話。 部分會話登錄方式與單系統登錄方式相同。這個過程,也就是單點登錄的原理,如下圖所示
下面對上圖進行簡單說明。
1、用戶訪問系統1的受保護資源,系統1發現用戶未登錄,跳轉到SSO認證中心,并以自己的地址作為參數;
2、SSO認證中心發現用戶未登錄,引導用戶至登錄頁面;
3、用戶輸入用戶名和密碼提交登錄申請;
4、SSO認證中心驗證用戶信息,在用戶和SSO認證中心之間創建會話(稱為全局會話),并創建授權令牌;
5、SSO認證中心會帶著token跳轉到原來的請求地址(系統1);
6、系統1拿到token,去SSO認證中心驗證token是否有效;
7、SSO認證中心驗證token,返回有效,注冊系統1;
8、系統1使用令牌與用戶創建會話,稱為部分會話,并返回受保護的資源;
9、用戶訪問系統2的受保護資源;
10、系統2發現用戶未登錄,跳轉到SSO認證中心,并使用自己的地址作為參數;
11、SSO認證中心發現用戶已登錄,跳轉回系統2的地址,并附上token;
12、系統2拿到token,去SSO認證中心驗證token是否有效;
13、SSO認證中心驗證token,返回有效,注冊系統2;
14. 系統 2 使用令牌與用戶創建部分會話并返回受保護的資源。
用戶成功登錄后,將與SSO認證中心及各子系統建立會話。 用戶與SSO認證中心建立的會話稱為全局會話。 用戶與各子系統建立的會話稱為本地會話。 本地會話建立后,用戶訪問子系統的受保護資源將不再經過SSO認證中心。 全局會話和本地會話有以下限制:
1、如果本地會話存在,則全局會話也必須存在;
2、全局會話存在,但本地會話可能不存在;
3、全局會話銷毀,本地會話也必須銷毀。
您可以通過博客園、百度、csdn、淘寶等網站的登錄流程加深對單點登錄的理解。 登錄過程中注意跳轉URL和參數。
2. 退出
當然,單點登錄也需要單點注銷。 如果您在某個子系統中注銷,則所有子系統的會話都將被銷毀。 用下圖來說明
SSO認證中心始終監控全局會話的狀態。 一旦全局會話被銷毀,監聽器將通知所有注冊系統執行注銷操作。
下面對上圖進行簡單說明。
1、用戶向系統1發起注銷請求;
2、系統1根據用戶與系統1建立的會話ID獲取token,并向SSO認證中心發起注銷請求;
3、SSO認證中心驗證token有效,銷毀全局會話,并刪除所有用該token注冊的系統地址;
4、SSO認證中心向所有注冊系統發起注銷請求;
5、各注冊系統收到SSO認證中心的注銷請求,銷毀部分會話;
6. sso認證中心引導用戶進入登錄頁面。
04部署圖
單點登錄涉及到SSO認證中心和眾多子系統。 子系統和SSO認證中心需要通信來交換令牌、驗證令牌并發起注銷請求。 因此,子系統必須集成SSO客戶端,SSO認證中心就是SSO。 在服務器端英語作文,整個單點登錄過程本質上就是SSO客戶端和服務器之間的通信過程,如下圖所示
SSO認證中心與SSO客戶端之間的通信方式有多種。 這里我們以一個簡單易用的為例。 Web、RPC、API 都可用。
05 實施
這里只是簡單介紹一下基于Java的實現過程,沒有提供完整的源碼。 一旦你明白了原理,我相信你可以自己實現。 單點登錄采用客戶端/服務器架構。 我們先來看看sso-和sso-要實現的功能(下圖:sso認證中心=sso-)。
單點登錄-
1、攔截子系統內非登錄用戶的請求,并跳轉至SSO認證中心;
2、接收并存儲SSO認證中心發送的token;
3、與sso-通信,驗證token的有效性;
4、建立本地會話;
5、攔截用戶的注銷請求,并將注銷請求發送至SSO認證中心;
6. 接收SSO認證中心下發的下線請求,銷毀部分會話。
單點登錄-
1、驗證用戶的登錄信息;
2. 創建全局會話;
3. 創建授權令牌;
4、與sso通信——發送token;
5、驗證sso-token的有效性;
6、系統注冊;
7. 接收 sso- 請求并注銷所有會話。
接下來我們就按照原理一步步實現sso吧!
1. sso-攔截非登錄請求
java中攔截請求的方式有3種,我們分別使用。 在sso-中新建一個.java類并實現接口,在()方法中添加對未登錄用戶的攔截。
public?void?doFilter(ServletRequest?request,?ServletResponse?response,?FilterChain?chain)?throws?IOException,?ServletException?{
????HttpServletRequest?req?=?(HttpServletRequest)?request;
????HttpServletResponse?res?=?(HttpServletResponse)?response;
????HttpSession?session?=?req.getSession();
????if?(session.getAttribute("isLogin"))?{
????????chain.doFilter(request,?response);
????????return;
????}
????//跳轉至sso認證中心
????res.sendRedirect("sso-server-url-with-system-url");
}
2. sso-攔截非登錄請求
攔截sso-到sso認證中心的非登錄請求,并跳轉到登錄頁面。 這個過程和sso-完全一樣。
3.sso-驗證用戶登錄信息
用戶在登錄頁面輸入用戶名和密碼,請求登錄。SSO認證中心對用戶信息進行驗證。 驗證成功,會話狀態標記為“已登錄”。
@RequestMapping("/login")
public?String?login(String?username,?String?password,?HttpServletRequest?req)?{
????this.checkLoginInfo(username,?password);
????req.getSession().setAttribute("isLogin",?true);
????return?"success";
}
4. sso-創建授權令牌
授權令牌是一串隨機字符。 它是如何生成的并不重要,只要它不重復且不能輕易偽造即可。 這是一個例子
String?token?=?UUID.randomUUID().toString();
5.sso-獲取token并驗證
登錄SSO認證中心后,跳轉回子系統并附加token。 子系統(sso-)獲取到tokenverify什么意思,然后去SSO認證中心進行驗證,在.java中的()中添加幾行
//?請求附帶token參數
String?token?=?req.getParameter("token");
if?(token?!=?null)?{
????//?去sso認證中心校驗token
????boolean?verifyResult?=?this.verify("sso-server-verify-url",?token);
????if?(!verifyResult)?{
????????res.sendRedirect("sso-server-url");
????????return;
????}
????chain.doFilter(request,?response);
}
這里僅簡單介紹一下()方法的實現。 詳細使用方法請參考官方文檔。
HttpPost?httpPost?=?new?HttpPost("sso-server-verify-url-with-token");
HttpResponse?httpResponse?=?httpClient.execute(httpPost);
6. sso-接收并處理驗證令牌請求
用戶成功登錄sso認證中心后,sso-創建授權令牌并存儲該令牌。 因此,ss-驗證令牌以查明令牌是否存在以及是否已過期。 token驗證成功后,sso-將發送驗證請求的系統注冊到SSO認證中心(即存儲)
令牌和注冊系統地址通常存儲在鍵值數據庫(例如redis)中。 Redis可以為key設置有效期,也就是token的有效期。 Redis 在內存中運行并且速度非常快,就像 SSO 一樣 - 不需要持久化數據。
令牌和注冊系統地址可以使用下圖描述的結構存儲在redis中。 你可能會問,為什么要存儲這些系統的地址?如果不存儲的話,注銷的時候會很麻煩。 用戶向SSO認證中心提交注銷請求,SSO認證中心注銷全局會話。 然而,它不知道哪些系統已經使用這個全局會話建立了自己的本地會話,也不知道需要將哪些子會話發送到SSO認證中心。 系統發送注銷請求,注銷本地會話
7. sso- token成功創建本地會話
令牌驗證成功后,sso將當前本地會話標記為“已登錄”,修改.java,添加幾行
if?(verifyResult)?{
????session.setAttribute("isLogin",?true);
}
sso-還需要將當前 id綁定到token上,也就是說這個的登錄狀態是和token相關的。 這個關系可以用java保存,保存的數據用來處理sso認證中心發送的注銷請求。
8. 注銷流程
用戶向子系統發送帶有“”參數的請求(注銷請求),sso攔截器攔截該請求并向sso認證中心發起注銷請求
String?logout?=?req.getParameter("logout");
if?(logout?!=?null)?{
????this.ssoServer.logout(token);
}
sso認證中心也用同樣的方法識別出該sso-是注銷請求(帶有“”參數),sso認證中心注銷全局會話。
@RequestMapping("/logout")
public?String?logout(HttpServletRequest?req)?{
????HttpSession?session?=?req.getSession();
????if?(session?!=?null)?{
????????session.invalidate();//觸發LogoutListener
????}
????return?"redirect:/";
}
SSO認證中心有一個全局會話監聽器。 一旦全局會話注銷,所有注冊的系統都會收到注銷通知。
public?class?LogoutListener?implements?HttpSessionListener?{
????@Override
????public?void?sessionCreated(HttpSessionEvent?event)?{}
????@Override
????public?void?sessionDestroyed(HttpSessionEvent?event)?{
????????//通過httpClient向所有注冊系統發送注銷請求
????sso認證中心有一個全局會話的監聽器,一旦全局會話注銷,將通知所有注冊系統注銷
福利時間:
前線推出學習交流群一定要備注:研究/工作方向+地點+學校/公司+昵稱(如目標檢測+上海+上交+卡卡),根據格式備注,可更快被通過且邀請進群 掃碼加我微信和進群和大佬們零距離(
(畢業后一直游離在互聯網大廠)
END 開發者技術前線 ,匯集技術前線快訊和關注行業趨勢,大廠干貨,是開發者經歷和成長的優秀指南。 歷史推薦
支付寶 App 架構的原理與實戰 為什么我不建議程序員做“外包”? 面試官:面阿里P7是吧?消息隊列這些我必問! 12306 系統架構到底有多牛? 除了Postman之外,居然還有個Postwoman... 好文點個在看吧!