1. 程式人生 > >sso單點登入原理詳解(最後解說的比較好)

sso單點登入原理詳解(最後解說的比較好)

yale cas可以百度一下,這是學習cas後的一點總結,以備日後使用!

安全性:

使用者只須在cas錄入使用者名稱和密碼,之後通過ticket繫結使用者,在cas客戶端與cas校驗是通過ticket,並不會在網上傳輸密碼,所以可以保證安全性,密碼不被竊取

原理:1個cookie+N個session

CAS建立cookie在所有應用中登入時cas使用,各應用通過在IE建立各自的session來標識應用是否已經登入。

Cookie:在cas為各應用登入時使用,實現了只須一次錄入使用者密碼

Session:各應用會建立自己的session表示是否登入

登入

1. CAS 登入時處理:

第一步:cas往瀏覽器增加cookie(TGC)

CAS向瀏覽器送回一個所謂的“記憶體cookie”。這種cookie並不是真的儲存在記憶體中,而只是瀏覽器一關閉,cookie就自動過期。這個cookie稱為“ticket-granting cookie”,用來表明使用者已經成功地登入。

這個Cookie是一個加密的Cookie,其中儲存了使用者登入的資訊。用於以後其它應用客戶端登入。

第二步:cas同時建立一個ticket重定向到原來的cas客戶端

認證成功後,CAS伺服器建立一個很長的、隨機生成的字串,稱為“Ticket”。隨後,CAS將這個ticket和成功登入的使用者,以及服務聯絡在一起。這個ticket是一次性使用的一種憑證,它只對登入成功的使用者及其服務使用一次。使用過以後立刻失效。

2. Cas 客戶端應用的處理

第一步:收到ticket後,向cas提交驗證ticket

Cas客戶端收到ticket之後,應用程式需要驗證ticket。這是通過將ticket 傳遞給一個校驗URL來實現的。校驗URL也是CAS伺服器提供的。CAS通過校驗路徑獲得了ticket之後,通過內部的資料庫對其進行判斷。如果判斷是有效性,則返回一個NetID給應用程式。隨後CAS將ticket作廢,並且在客戶端留下一個cookie。(誰來建立cookie?),

第二步:ticket驗證後建立session

   以後登入此應用時,沒有ticket,但IE能提供session,從session中取得CASReceipt,並驗證如果有效說明已經在此應用認證過,允許訪問此應用,

到此為止,CAS會記錄使用者已在應用A已經登入

3. 使用者登入到應用是如何處理

使用者進入應用B時,首先仍然會重定向到CAS伺服器。不過此時CAS伺服器不再要求使用者輸 入使用者名稱和密碼,而是首先自動尋找Cookie,根據Cookie中儲存的資訊,進行登入。然後,CAS同樣給出新的ticket重定向應用B給cas驗證(流程同應用A驗證方式),如果驗證成功則應用B建立session記錄CASReceipt資訊到session中,以後憑此session登入應用B。

到此為止,CAS會記錄使用者已在應用A和應用B進行登入,但是當用戶在應用B退出cas登入時,要通知應用A進行退出,如何通知應用A呢?

登出

  CAS server接受請求後,會檢測使用者的TCG Cookie,把對應的session清除,同時會找到所有通過該TGC sso登入的應用伺服器URL提交請求,所有的回撥請求中,包含一個引數logoutRequest,內容格式如下:

<samlp:LogoutRequest ID="[RANDOM ID]" Version="2.0" IssueInstant="[CURRENT DATE/TIME]">
<saml:NameID>@[email protected]</saml:NameID>
<samlp:SessionIndex>[SESSION IDENTIFIER]</samlp:SessionIndex>
</samlp:LogoutRequest>

所有收到請求的應用伺服器application會解析這個引數,取得sessionId,根據這個Id取得session後,把session刪除。
這樣就實現單點登出的功能。

知道原理後,下面是結合原始碼來講述一下內部的程式碼怎麼實現的。

客戶端實現:

首先,要實現single sign out在 應用伺服器application端的web.xml要加入以下配置
<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
注:如果有配置CAS client Filter,則CAS Single Sign Out Filter 必須要放到CAS client Filter之前。

配置部分的目的是在CAS server回撥所有的application進行單點登出操作的時候,需要這個filter來實現session清楚。

主要程式碼如下:
org.jasig.cas.client.session.SingleSignOutFilter
1 public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain 
2 
3 filterChain) throws IOException, ServletException {
4 final HttpServletRequest request = (HttpServletRequest) servletRequest;
5 
6 if ("POST".equals(request.getMethod())) {
7 final String logoutRequest = request.getParameter("logoutRequest");
8 
9 if (CommonUtils.isNotBlank(logoutRequest)) {
10 
11 if (log.isTraceEnabled()) {
12 log.trace ("Logout request=[" + logoutRequest + "]");
13 }
14 //從xml中解析 SessionIndex key值15 final String sessionIdentifier = XmlUtils.getTextForElement(logoutRequest, "SessionIndex");
16 
17 if (CommonUtils.isNotBlank(sessionIdentifier)) {
18 //根據sessionId取得session物件19 final HttpSession session = SESSION_MAPPING_STORAGE.removeSessionByMappingId(sessionIdentifier);
20 
21 if (session != null) {
22 String sessionID = session.getId();
23 
24 if (log.isDebugEnabled()) {
25 log.debug ("Invalidating session [" + sessionID + "] for ST [" + sessionIdentifier + "]");
26 }
27 
28 try {
29 //讓session失效30 session.invalidate();
31 } catch (final IllegalStateException e) {
32 log.debug(e,e);
33 }
34 }
35 return;
36 }
37 }
38 } else {//get方式 表示登入,把session物件放到SESSION_MAPPING_STORAGE(map物件中)39 final String artifact = request.getParameter(this.artifactParameterName);
40 final HttpSession session = request.getSession();
41 
42 if (log.isDebugEnabled() && session != null) {
43 log.debug("Storing session identifier for " + session.getId());
44 }
45 if (CommonUtils.isNotBlank(artifact)) {
46 SESSION_MAPPING_STORAGE.addSessionById(artifact, session);
47 }
48 }
49 
50 filterChain.doFilter(servletRequest, servletResponse);
51 }
SingleSignOutHttpSessionListener實現了javax.servlet.http.HttpSessionListener介面,用於監聽session銷燬事件
1 public final class SingleSignOutHttpSessionListener implements HttpSessionListener {
2 
3 private Log log = LogFactory.getLog(getClass());
4 
5 private SessionMappingStorage SESSION_MAPPING_STORAGE;
6 
7 public void sessionCreated(final HttpSessionEvent event) {
8 // nothing to do at the moment9 }
10 
11 //session銷燬時12 public void sessionDestroyed(final HttpSessionEvent event) {
13 if (SESSION_MAPPING_STORAGE == null) {//如果為空,建立一個sessionMappingStorage 物件14 SESSION_MAPPING_STORAGE = getSessionMappingStorage();
15 }
16 final HttpSession session = event.getSession();//取得當然要銷燬的session物件17 
18 if (log.isDebugEnabled()) {
19 log.debug("Removing HttpSession: " + session.getId());
20 }
21 //從SESSION_MAPPING_STORAGE map根據sessionId移去session物件22 SESSION_MAPPING_STORAGE.removeBySessionById(session.getId());
23 }
24 
25 /**26 * Obtains a {@link SessionMappingStorage} object. Assumes this method will always return the same
27 * instance of the object. It assumes this because it generally lazily calls the method.
28 
29 @return the SessionMappingStorage
30 */
31 protected static SessionMappingStorage getSessionMappingStorage() {
32 return SingleSignOutFilter.getSessionMappingStorage();
33 }
34 }

伺服器端實現

 已經登入的應用會在伺服器端儲存,所以服務端分別對各個應用傳送http請求進行session清除操作。

網上參考資料

看了下面的瀏覽器cookie變化,會對cas有更深的理解

下載個httpwatch監控一下cookie的變化

客戶端訊息流程

CLIENT:沒票據且SESSION中沒有訊息所以跳轉至CAS

CAS:拿不到TGC故要求使用者登入

2. 認證成功後回跳

CLIENT:帶有票據所以不跳轉只是後臺發給CAS驗證票據(瀏覽器中無法看到這一過程)

CLIENT:沒票據且SESSION中沒有訊息所以跳轉至CAS

CAS:從客戶端取出TGC,如果TGC有效則給使用者ST並後臺驗證ST,從而SSO。【如果失效重登入或登出時,怎麼通知其它系統更新SESSION資訊呢??TicketGrantingTicketImpl類grantServiceTicket方法裡this.services.put(id,service);可見CAS端已經記錄了當前登入的子系統】

CLIENT:沒票據但是SESSION中有訊息故不跳轉也不用發CAS驗證票據,允許使用者訪問