1. 程式人生 > >CAS5.2x單點登入(七)-------------單點退出

CAS5.2x單點登入(七)-------------單點退出

單點登出產生的原因

前幾篇我們已經講了如何搭建單點登入以及他的服務註冊等,我們也清楚單點登入的目的是為了不用頻繁的去登入,基本上有單點登入的地方就有單點登出,因為這兩個感覺是繫結在一起的。我們知道cas單點登入成功後,cas會為我們每個客戶端生成一個session,然後再次訪問這個客戶端的介面的時候,我們客戶端引入的cas的jar包會去判斷,這個介面是否有session,如果沒有的話就繼續判斷下面的,如果有的話就直接跳過。但是我們想象一下,當我們點選caslogout的時候,發現其他子系統還能登陸,但是卻不能再次免登入到其他的系統。這可能就有點怪怪的,單點登入不是一個登入,接入進來的子系統就免登入了嗎?事實是這樣的,但是cas的原理其實是靠tgt來記錄登入的資訊,也就是當我們登入的時候,我們瀏覽器會記錄一個tgt,下次子系統登入的時候,會帶著瀏覽器的tgt如果存在的話,那麼就授予票據,之後的一系列操作就不說了。這樣就達到免登入的效果了。但是有這樣的一個場景:我們接入cas的子系統有3個,這時候我們通過cas單點登入開啟了兩個子系統,這時候我執行一下caslogout,我們發現已經登入上去的兩個子系統沒有受影響,但是當我們點選最後一個子系統的時候發現他需要登入。剛開始我還以為我自己弄錯了,但是試了好久還是這個問題。我們上面說了tgt的事情(可以自己去了解一下cas單點登入的原理),我們執行caslougout的時候其實就是將cas的登入tgt給只為無效,所以當我們第三個子系統去登入的時候,cas發現他沒有session,同時也沒有st,這個時候就會轉到cas登入頁面,但是如果cas已經登入的話那麼就能直接獲取到st,可是因為我們之前的退出已經將cas的tgt給只為無效了,所以cas發現沒有登入,就要去重新登入。但是這樣客戶一定不會滿意,因為他們要的效果是,一個退出其他的都要退出,一個登入其他的都要登入,這時候就需要單點退出了,也就是我接下來要講的內容。

單點登出的原理

單點登出的原理其實不難,cas他其實已經幫我們實現了這個退出的功能,我畫了個流程圖便於大家理解

這裡寫圖片描述
他的退出流程其實就是上面的這個流程圖,至於他是如何退出的,你可以參考一下singleout的原始碼,其實也不是很難,如果瞭解cas的原理的話,可能知道st,其實在cas寫session的時候其實已經將st也同時給儲存下來了,刪除的時候也會通過st去刪除。這個原始碼裡面有,如果需要的話,我有時間可以解讀一下原始碼。

退出的url在哪裡設定?

上面說了cas退出的流程,我們發現其中有個logouturl,這個是在哪設定的呢?
其實cas的退出在官方文件中已經寫過了,
cas的退出有三種模式:

  • NONE:不支援單點登入
  • BACK_CHANNEL:隱式退出(預設)
  • FRONT_CHANNEL:顯式退出
    第一種我們就不說了:
    隱式退出:cas傳送通知,業務系統後端主動登出使用者
    顯式退出:cas傳送資料給客戶端,客戶端接收並且登出使用者,再轉發到cas往下處理登出流程
    而我們cas預設採用的是第二種,我也就用第二種來講解。
    我們設定退出的url有好多種方式,但是原理都是一樣的。我們先使用json格式檔案來說,
"@class": "org.apereo.cas.services.RegexRegisteredService",
  "serviceId" : "^(https|imaps|http)://localhost:8080.*"
, "name": "name", "id": 10000010, "description": "desc", "evaluationOrder": 1, "logoutUrl": "http://localhost:8080"

我們看到上面的一個logouturl的設定,其實他設定的也沒有多難,只需要這樣設定就ok了,上面的描述檔案的各個欄位就不用我多說了吧,如果我們要選擇模式的話就要自己在這上面加入一個模式,你也可以參考cas的文件進行填寫,cas的文件已經很詳細的說明了哪些是必填的哪些是選填的。
上面使用的json檔案的方式,對於我來說總感覺有點麻煩,我這邊使用的jpa資料庫儲存的方式,其實原理以及欄位都是一致的:我們使用restful風格的介面去註冊這樣的服務

@RequestMapping(value = "/addClient/{protocol}/{serviceId}/{id}",method = RequestMethod.GET) 
      public String addClient(@PathVariable("serviceId") String serviceId,@PathVariable("protocol") String protocol
              ,@PathVariable("id") int id) throws IOException {
          String url=protocol+"://"+serviceId;
          RegisteredService svc = servicesManager.findServiceBy(url);
          if(svc!=null){
              return "0";//0代表著已存在這個服務,服務是通過正則去匹配的,所以這邊建議使用ip或者域名+埠號
          }
          String a="^"+url+".*";//匹配以這個url開始的url
          RegexRegisteredService service=new RegexRegisteredService();
          ReturnAllAttributeReleasePolicy re=new ReturnAllAttributeReleasePolicy();
          service.setServiceId(a);
          service.setId(id);
          service.setAttributeReleasePolicy(re);
          //將name統一設定為servicesId
          service.setName(serviceId);
          service.setLogoutType(LogoutType.BACK_CHANNEL);
          service.setLogoutUrl(new URL(url));//這個是為了單點登出而作用的
          servicesManager.save(service);
          servicesManager.load();
          return "1";//新增服務成功
    }

到這裡我們cas的退出也已經講完了,也可以實現單點退出的功能,但是對於分散式的話,就會出現問題,因為分散式的系統的話是為了負載均衡,所以退出的時候可能執行的是另一個系統,可能導致session不成功,這邊的話我還要去看看怎麼解決這個問題