單一登入原理和簡單實現
一、單一系統登入機制
1、http#無狀態協定
web應用採用browser/ server架構,http作為通訊協定。 http是無狀態協議,瀏覽器的每一個請求,服務器會獨立處理,不與之前或之後的請求產生關聯,這個過程用下圖說明,三次請求/回應對之間沒有任何聯繫
但這也同時意味著,任何使用者都能透過瀏覽器存取伺服器資源,如果想保護伺服器的某些資源,必須限制瀏覽器請求;要限制瀏覽器請求,必須鑑別瀏覽器請求,回應合法請求,忽略非法請求;要鑑別瀏覽器請求,必須清楚瀏覽器請求狀態。既然http協定無狀態,那就讓伺服器和瀏覽器共同維護一個狀態吧!這就是會話機制
2、會話機制
瀏覽器第一次請求伺服器,伺服器建立一個會話,並將會話的id作為回應的一部分傳送給瀏覽器,瀏覽器存儲會話id,並在後續第二次和第三次請求中帶上會話id,伺服器取得請求中的會話id就知道是不是同一個用戶了,這個過程用下圖說明,後續請求與第一次請求產生了關聯
伺服器在記憶體中保存會話物件,瀏覽器怎麼保存會話id呢?你可能會想到兩種方式
# 將會話id作為每一個請求的參數,伺服器接收請求自然能解析參數獲得會話id,並藉此判斷是否來自同一會話,很明顯,這種方式不靠譜。那就瀏覽器自己來維護這個會話id吧,每次發送http請求時瀏覽器自動發送會話id,cookie機制正好用來做這件事。 cookie是瀏覽器用來儲存少量資料的機制,資料以」key/value「形式存儲,瀏覽器發送http請求時自動附帶cookie訊息
tomcat會話機制當然也實作了cookie,造訪tomcat伺服器時,瀏覽器中可以看到一個名為「JSESSIONID」的cookie,這就是tomcat會話機制維護的會話id,使用了cookie的請求回應流程如下圖
3、登入狀態
有了會話機制,登入狀態就好明白了,我們假設瀏覽器第一次請求伺服器需要輸入使用者名稱與密碼驗證身份,伺服器拿到用戶名密碼去資料庫比對,正確的話說明目前持有這個會話的用戶是合法用戶,應該將這個會話標記為「已授權」或「已登入」等等之類的狀態,既然是會話的狀態,自然要保存在會話對像中,tomcat在會話對像中設定登入狀態如下
HttpSession session = request.getSession(); session.setAttribute("isLogin", true);
使用者再次造訪時,tomcat在會話對象中查看登入狀態
HttpSession session = request.getSession(); session.getAttribute("isLogin");
實作了登入狀態的瀏覽器請求伺服器模型如下圖描述
每次請求受保護資源時都會檢查會話物件中的登入狀態,只有isLogin=true 的會話才能訪問,登入機制因此而實現。
二、多系統的複雜性
web系統早已從久遠的單系統發展成為如今由多系統組成的應用群,面對如此眾多的系統,用戶難道要一個一個登入、然後一個一個註銷嗎?就像下圖描述的這樣
web系統由單一系統發展成多系統所組成的應用群,複雜度應該由系統內部承擔,而不是使用者。無論web系統內部多麼複雜,對使用者而言,都是一個統一的整體,也就是說,使用者存取web系統的整個應用群組與存取單一系統一樣,登入/登出只要一次就夠了
# 雖然單一系統的登入解決方案很完美,但對於多系統應用群組已經不再適用了,為什麼呢?
單系統登入解決方案的核心是cookie,cookie攜帶會話id在瀏覽器與伺服器之間維護會話狀態。但cookie是有限制的,這個限制就是cookie的網域(通常對應網站的網域),瀏覽器發送http請求時會自動攜帶與該網域相符的cookie,而不是所有cookie
既然這樣,為什麼不將web應用群中所有子系統的域名統一在一個頂級域名下,例如“*.baidu.com”,然後將它們的cookie域設置為“baidu.com” ,這種做法理論上是可以的,甚至早期很多多系統登入就採用這種同域名共享cookie的方式。
然而,可行並不代表好,共享cookie的方式存在眾多限制。首先,應用群組網域名稱得統一;其次,應用群組各系統使用的技術(至少是web伺服器)要相同,不然cookie的key值(tomcat為JSESSIONID)不同,無法維持會話,共享cookie的方式是無法實現跨語言技術平台登入的,例如java、php、.net系統之間;第三,cookie本身不安全。
因此,我們需要一種全新的登錄方式來實現多系統應用群的登錄,這就是單點登錄
#三、單點登錄
什麼是單點登入?單點登入全稱Single Sign On(以下簡稱SSO),是指在多系統應用群中登錄一個系統,便可在其他所有系統中得到授權而無需再次登錄,包括單點登錄與單點註銷兩部分
1、登入
相比於單系統登錄,sso需要一個獨立的認證中心,只有認證中心能接受用戶的用戶名密碼等安全信息,其他系統不提供登錄入口,只接受認證中心的間接授權。間接授權透過令牌實現,sso認證中心驗證使用者的使用者名稱密碼沒問題,建立授權令牌,在接下來的跳轉過程中,授權令牌作為參數傳送給各個子系統,子系統拿到令牌,即得到了授權,可以藉此建立局部會話,局部會話登入方式與單一系統的登入方式相同。這個過程,也就是單一登入的原理,用下圖說明
下面對上圖簡單描述
用戶訪問系統1的受保護資源,系統1發現用戶未登錄,跳到sso認證中心,並將自己的地址作為參數
sso認證中心發現用戶未登錄,將使用者引導至登入頁面
使用者輸入使用者名稱密碼提交登入申請
sso認證中心校驗使用者訊息,建立使用者與sso認證中心之間的會話,稱為全域會話,同時建立授權令牌
sso認證中心帶著令牌跳到最初的請求位址(系統1)
系統1拿到令牌,去sso認證中心校驗令牌是否有效
#sso認證中心校驗令牌,返回有效,註冊系統1
系統1使用該令牌建立與使用者的會話,稱為局部會話,傳回受保護資源
使用者存取系統2的受保護資源
系統2發現使用者未登錄,跳到sso認證中心,並將自己的位址作為參數
- ##sso認證中心發現使用者已登錄,跳到系統2的位址,並附上令牌
- 系統2拿到令牌,去sso認證中心校驗令牌是否有效
- sso認證中心校驗令牌,傳回有效,註冊系統2
- 系統2使用該令牌建立與使用者的局部會話,返回受保護資源
用户登录成功之后,会与sso认证中心及各个子系统建立会话,用户与sso认证中心建立的会话称为全局会话,用户与各个子系统建立的会话称为局部会话,局部会话建立之后,用户访问子系统受保护资源将不再通过sso认证中心,全局会话与局部会话有如下约束关系
局部会话存在,全局会话一定存在
全局会话存在,局部会话不一定存在
全局会话销毁,局部会话必须销毁
你可以通过博客园、百度、csdn、淘宝等网站的登录过程加深对单点登录的理解,注意观察登录过程中的跳转url与参数
2、注销
单点登录自然也要单点注销,在一个子系统中注销,所有子系统的会话都将被销毁,用下面的图来说明
sso认证中心一直监听全局会话的状态,一旦全局会话销毁,监听器将通知所有注册系统执行注销操作
下面对上图简要说明
用户向系统1发起注销请求
系统1根据用户与系统1建立的会话id拿到令牌,向sso认证中心发起注销请求
sso认证中心校验令牌有效,销毁全局会话,同时取出所有用此令牌注册的系统地址
sso认证中心向所有注册系统发起注销请求
各注册系统接收sso认证中心的注销请求,销毁局部会话
sso认证中心引导用户至登录页面
四、部署图
单点登录涉及sso认证中心与众子系统,子系统与sso认证中心需要通信以交换令牌、校验令牌及发起注销请求,因而子系统必须集成sso的客户端,sso认证中心则是sso服务端,整个单点登录过程实质是sso客户端与服务端通信的过程,用下图描述
sso认证中心与sso客户端通信方式有多种,这里以简单好用的httpClient为例,web service、rpc、restful api都可以
五、实现
只是简要介绍下基于java的实现过程,不提供完整源码,明白了原理,我相信你们可以自己实现。sso采用客户端/服务端架构,我们先看sso-client与sso-server要实现的功能(下面:sso认证中心=sso-server)
sso-client
拦截子系统未登录用户请求,跳转至sso认证中心
接收并存储sso认证中心发送的令牌
与sso-server通信,校验令牌的有效性
建立局部会话
拦截用户注销请求,向sso认证中心发送注销请求
接收sso认证中心发出的注销请求,销毁局部会话
sso-server
验证用户的登录信息
创建全局会话
创建授权令牌
与sso-client通信发送令牌
校验sso-client令牌有效性
系统注册
接收sso-client注销请求,注销所有会话
接下来,我们按照原理来一步步实现sso吧!
1、sso-client拦截未登录请求
java拦截请求的方式有servlet、filter、listener三种方式,我们采用filter。在sso-client中新建LoginFilter.java类并实现Filter接口,在doFilter()方法中加入对未登录用户的拦截
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-server拦截未登录请求
拦截从sso-client跳转至sso认证中心的未登录请求,跳转至登录页面,这个过程与sso-client完全一样
3、sso-server验证用户登录信息
用户在登录页面输入用户名密码,请求登录,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-server创建授权令牌
授权令牌是一串随机字符,以什么样的方式生成都没有关系,只要不重复、不易伪造即可,下面是一个例子
String token = UUID.randomUUID().toString();
5、sso-client取得令牌并校验
sso认证中心登录后,跳转回子系统并附上令牌,子系统(sso-client)取得令牌,然后去sso认证中心校验,在LoginFilter.java的doFilter()中添加几行
// 请求附带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); }
verify()方法使用httpClient实现,这里仅简略介绍,httpClient详细使用方法请参考官方文档
HttpPost httpPost = new HttpPost("sso-server-verify-url-with-token"); HttpResponse httpResponse = httpClient.execute(httpPost);
6、sso-server接收并处理校验令牌请求
用户在sso认证中心登录成功后,sso-server创建授权令牌并存储该令牌,所以,sso-server对令牌的校验就是去查找这个令牌是否存在以及是否过期,令牌校验成功后sso-server将发送校验请求的系统注册到sso认证中心(就是存储起来的意思)
令牌与注册系统地址通常存储在key-value数据库(如redis)中,redis可以为key设置有效时间也就是令牌的有效期。redis运行在内存中,速度非常快,正好sso-server不需要持久化任何数据。
令牌与注册系统地址可以用下图描述的结构存储在redis中,可能你会问,为什么要存储这些系统的地址?如果不存储,注销的时候就麻烦了,用户向sso认证中心提交注销请求,sso认证中心注销全局会话,但不知道哪些系统用此全局会话建立了自己的局部会话,也不知道要向哪些子系统发送注销请求注销局部会话
7、sso-client校验令牌成功创建局部会话
令牌校验成功后,sso-client将当前局部会话标记为“已登录”,修改LoginFilter.java,添加几行
if (verifyResult) { session.setAttribute("isLogin", true); }
sso-client还需将当前会话id与令牌绑定,表示这个会话的登录状态与令牌相关,此关系可以用java的hashmap保存,保存的数据用来处理sso认证中心发来的注销请求
8、注销过程
用户向子系统发送带有“logout”参数的请求(注销请求),sso-client拦截器拦截该请求,向sso认证中心发起注销请求
String logout = req.getParameter("logout"); if (logout != null) { this.ssoServer.logout(token); }
sso认证中心也用同样的方式识别出sso-client的请求是注销请求(带有“logout”参数),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向所有注册系统发送注销请求 } }
以上是單一登入原理和簡單實現的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題

如何透過PHP編寫一個簡單的線上問卷調查系統隨著網路的發展和普及,越來越多的人開始傾向於透過網路來進行各種調查和問卷調查。為了滿足這項需求,我們可以透過PHP語言編寫一個簡單的線上問卷調查系統。本文將介紹如何使用PHP實現一個基本的問卷調查系統,並提供具體的程式碼範例。資料庫設計首先,我們需要設計一個資料庫來儲存問卷調查的相關資料。我們可以使用MySQL數據

如何使用PHP實現高效穩定的SSO單一登入引言:隨著網路應用的普及,使用者面臨大量的註冊和登入流程。為了提高使用者體驗,並減少使用者的註冊和登入間隔,許多網站和應用程式開始採用單一登入(SingleSign-On,簡稱SSO)技術。本文將介紹如何使用PHP實現高效穩定的SSO單一登錄,並提供具體的程式碼範例。一、SSO單一登入原理SSO單一登入是一種身分認證的解決

如何使用PHP實現一個簡單的線上訂餐系統隨著網路的普及,訂餐產業也逐漸朝向線上發展。為了滿足使用者的需求,線上訂餐系統的開發變得非常重要。本文將介紹如何使用PHP語言來實現一個簡單的線上訂餐系統,並提供具體的程式碼範例。定義資料庫結構首先,我們需要定義資料庫結構來儲存訂單資訊。建立一個名為"orders"的資料表,並定義以下欄位:order_id:訂單ID

GitLab的權限管理和單一登入整合技巧,需要具體程式碼範例概述:在GitLab中,權限管理和單一登入(SSO)是非常重要的功能。權限管理可以控制使用者對程式碼倉庫、專案和其他資源的存取權限,而單一登入整合可以提供更方便的使用者認證和授權方式。本文將介紹如何在GitLab中進行權限管理和單一登入整合。一、權限管理專案存取權限控制在GitLab中,專案可以設定為私有

如何使用PHP開發簡單的商品評論功能隨著電子商務的興起,商品評論功能成為了一個不可或缺的功能,方便用戶之間的交流和消費者對商品的評價。本文將介紹如何使用PHP開發一個簡單的商品評論功能,並附上具體的程式碼範例。創建資料庫首先,我們需要建立一個資料庫來儲存商品評論資訊。建立一個名為「product_comments」的資料庫,並在其中建立一個名為「comment

今天,我們將透過使用PHP程式語言來實現一個簡單的線上點餐系統。 PHP是一種流行的伺服器端腳本語言,它非常適合用於開發基於Web的應用程式。我們將展示如何使用PHP創建一個簡單的點餐系統,用戶可以在網站上選擇菜餚,並將它們添加到他們的購物車中,最後完成交易。在開始之前,我們需要建立一個新的PHP項目,並安裝必要的依賴項,例如資料庫和伺服器環境。我們將使用MySQ

如何使用PHP開發簡單的商品比較功能,需要具體程式碼範例隨著電商的發展,用戶在購物時經常會遇到選擇商品的困擾,例如不知道該選擇哪個牌子的產品更好,哪個店舖的價格更實惠等。為了解決這個問題,我們可以發展一個簡單的商品比較功能,幫助使用者方便地比較商品的各項屬性,並從中做出選擇。本文將介紹如何使用PHP來實現這個功能,並給出具體的程式碼範例。首先,我們需要創建一個商品

如何設計一個安全的MySQL表結構來實現單一登入功能?隨著互聯網的發展,用戶在不同的應用程式中需要登入不同的帳戶成為一種常見情況。為了提升使用者體驗與便利性,單一登入(SingleSign-On,簡稱SSO)技術應運而生。 SSO技術使得用戶可以透過一次登入存取多個應用程序,避免了頻繁輸入帳戶和密碼的麻煩。在設計一個安全的MySQL表結構來實現單一登入功能
