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