.NET、JAVA集成CAS認證超簡單方案,無需CAS源碼保留應用自身驗證

先說下本次.NET網站應用與CAS認證集成的特點

  1. CAS-服務器端默認是已經搭建完畢,且正常運行的,本次方案只針對CAS客戶端如何集成介紹。
  2. 無需CAS源碼,也無需深層研究CAS服務器端原理,不需要進行繁瑣的配置文件修改調整。
  3. 保留應用內部用戶信息存儲和驗證方式不變,如form認證等可以繼續保留。
  4. 對應用無需進行任何架構和功能調整,只需要對原登錄頁面簡單新增幾行代碼即可。
  5. 本次方案介紹一個CAS客戶端如何與CAS認證集成,最主要是介紹2個或多個CAS客戶端同時與CAS做集成。
  6. 本次集成方案是基於.NET環境通過C#語音中進行的代碼示例,同樣的邏輯在其他Web應用如JAVA、PHP等語言下是一樣通用的。
  7. 由於是與多個系統同時做集成,暫時沒有考慮如何登出註銷的問題,用戶瀏覽器關閉後,存儲的用戶信息自動消失。

事情起因是最近公司有個項目需要與CAS認證系統做單點集成,CAS服務器端用戶已經搭建完成。公司之前做過類似的單點集成,但是沒有直接與CAS做認證的,對CAS這塊不是很熟悉。所以一開始不打算做了,但是甲方一直要求做,然後給CAS的供應商要了接口文檔和說明,本來以為接口會很詳細明瞭,誰知道給的文檔都是如何部署JAVA服務器端的文檔。不懂JAVA,看文檔簡直是隔行如隔山,關鍵文檔是如何部署服務器端的,不是介紹第三方應用如何與CAS認證集成的。沒辦法,最終這個CAS認證我說還是我自己嘗試下吧。

然後就開始了百度、CSDN的苦逼搜索之路。網上一搜".NET集成CAS認證",搜索結果一大片,都有各種介紹和各種原理說明,其中大部分都是需要下載.net版本的 CAS源碼進行二次改造集成等等。

一看有源碼,好啊!抓緊下載源碼,先是下載了最新.NET版本CAS源碼,但是在開發環境無法打開,花費了不少時間解決這個問題,最終看到有個文章裡面說最新的需要VS2017才能打開,然後又下載了一個適配VS2013版本的源碼。

OK,這次可以打開正常調試了,很開心,覺得離成功越來越近了。然後按照介紹進行配置、調試,發佈到IIS進行測試。好吧,然後我才發現我掉入到坑裡面了。調試到各種問題,如重複定向問題、再次驗證失敗問題。花了3天時間把整個源碼邏輯一步步的跟蹤了很多遍,弄懂了大部分代碼的功能,但悲哀的是最終要實現的單點效果一直沒實現。

然後就繼續網上找資源,突然一個簡單的代碼示例出現在我的眼前

<code>\t\t\t\tstring CASHOST = "https://cas.test11.cn/cas/";   //cas服務器地址
string tkt = Request.QueryString["ticket"];
string service = Request.Url.GetLeftPart(UriPartial.Path);
if (tkt == null || tkt.Length == 0) //檢查未帶ticket,重定向到cas登錄頁
{
string redir = CASHOST + "login?service=" + service;

Response.Redirect(redir);
return;
}
string validateurl = CASHOST + "serviceValidate?ticket=" + tkt + "&service=" + service;
StreamReader Reader = new StreamReader(new WebClient().OpenRead(validateurl)); //根據ticket驗證取回用戶信息
string resp = Reader.ReadToEnd();
NameTable nt = new NameTable();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(nt);
XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
XmlTextReader reader = new XmlTextReader(resp, XmlNodeType.Element, context);
string netid = null;
while (reader.Read()) //從返回信息中讀取用戶賬號等
{
if (reader.IsStartElement())
{
string tag = reader.LocalName;
if (tag == "user")
netid = reader.ReadString();
//這裡可以讀取其它返回信息
}
}
reader.Close();
if (netid == null) //服務器拒絕驗證,未返回用戶信息
{
Response.Write("CAS returned to this application, but then refused to validate your identity.");
}
else //返回了用戶信息,做初始化成功登錄本軟件處理
{
Session["UserName"] = netid;
Response.Write("Welcome " + netid);
//此處添加應用內部原有登錄邏輯。即可登錄成功
}/<code>

看到這塊代碼後感覺豁然開朗,然後迫不及待的進行了嘗試。將CAS服務器地址改為用戶提供的測試地址,然後將以上代碼加入到現在項目的登錄頁面後臺,調試後一切OK。輸入應用系統的登錄地址後,可以直接跳轉到CAS登錄頁面,輸入用戶名密碼後即可正常跳轉回應用系統自己的首頁。

以上代碼的具體邏輯,相信大部分人都可以看明白,CAS認證的詳細原理這裡就不再贅述了,網上有很多介紹。這裡只簡單介紹下上面的認證過程,即一個CAS-Client如何與CAS-Server做集成。


暫以“Client1”代替CAS-Client。”Client1“與CAS認證的核心是ticket生成以及如何根據ticket獲取對應的用戶信息,只要明白了以上兩點,即可很快明白CAS認證的基本原理。

· Ticket如何生成的? Ticket是用戶終端請求CAS-Server端登錄成功後(情形一)或TGC驗證通過後(情形二)由CAS-Server生成的。

· 如何根據Ticket獲取對應的用戶信息?需要帶上票據Ticket和客戶端地址service,去訪問CAS-Server的serviceValidate接口進行數據獲取,完整請求示例為:【https://castest.cn/cas/serviceValidate?ticket=STSx6eyvj7cPPCfn0pMZ&service=https://Client1/iPortal/Login_admin.aspx】。獲取到返回數據後,返回數據格式為XML,解析其中的user節點,即為用戶賬號。

情形一:【一個CAS-Client與CAS-Server認證集成過程】

下面介紹具體認證過程:

1)終端第一次訪問Client1

  • 首先,檢測本地沒有緩存Client1的用戶信息;
  • 然後,檢測到請求信息中沒有Ticket憑據(即CAS為用戶簽發的訪問某一服務票據);
  • 所以,Client1將請求重定向到CAS—Server(https://cas.test.cn/cas/login),並傳遞 Service (也就是要訪問的系統A的登錄頁URL,以便登錄成功過後轉回該地址)示例【https://cas.test.cn/cas/login? Service =https://Client1/iPortal/Login_admin.aspx】

2)終端第一次訪問CAS—Server

  • CAS—Server檢測到請求信息中沒有TGC(即CAS登錄成功後在客戶端存儲的用戶信息),所以跳轉到自己的登錄頁;
  • 終端輸入用戶名、密碼,點擊登錄按鈕,認證成功後,CAS—Server會生成一個服務票據—Ticket與CAS會話標識—TGC。大家只需要知道Ticket即可,上面有介紹。
  • 然後,CAS—Server會將Ticket加在url 後面,將請求redirect 回客戶web 應用,例如URL為【https://Client1/iPortal/Login_admin.aspx?ticket=ST-5-Sx6eyvj7cPPCfn0pMZ】

3)終端攜帶ticket再次請求Client1

注:本次請求是通過CAS登錄頁面登錄成功後,由CAS服務器端重定向跳轉到的Client1。不屬於、也不需要用戶主動操作。

  • 這時Client1後臺系統看到ticket 參數後,會獲取此參數,由其後面的“票據驗證”功能進行邏輯處理,不再繼續往CAS服務器端進行跳轉。
  • Client1“票據驗證”功能實現邏輯為:通過http請求,訪問cas 服務的/serviceValidate 接口,將ticket 、service 都傳到此接口,由此接口驗證ticket 的有效性並返回用戶數據。請求接口示例為【https://cas.test.cn/cas/serviceValidate?ticket=ST0pMZ&service=https://Client1/iPortal/Login_admin.aspx】
  • CAS的serviceValidate 接口驗證成功後,會返回用戶賬號信息,Client1在此獲取到用戶賬號,然後把用戶賬號轉為Client1應用內部的Cookie或者Session用戶憑據。
  • 至此為止,SSO 會話就建立起來了。Client1登錄成功。

情形一的原理大家應該都可以看明白吧,也即代碼主要實現的功能。如果明白了上面那些邏輯,恭喜你,CAS認證你已經學會了一半,為什麼是一半呢?

因為上面的整個過程是Client1直接與CAS-Server端做的認證,同時也是第一次登錄認證,是在CAS登錄頁成功後獲取到ticket後進行的操作。那假如Client1系統已經與CAS認證成功了,現在有個Client2系統也需要與CAS做認證集成,如何在Client2中獲取到CAS已經認證成功的用戶信息呢?即下面介紹的【多個CAS-Client與CAS-Server認證集成過程】

情形二:【多個CAS-Client與CAS-Server認證集成過程】

因為Client1獲取得用戶賬號是在CAS登錄頁面登錄成功後又根據跳轉回傳的ticket獲取的,再訪問Client2時不應該再出現登錄頁面了,那如何獲取ticket呢?既然獲取不到ticket你是不是會想到有沒有其他方式可以可以獲取到已經認證的CAS用戶信息呢?

好吧,我就掉入到這麼一個新的坑裡面去了,想著去CAS源碼裡面查下有沒有其他方式獲取客戶端已經認證的用戶信息。然後又花了幾天時間繼續去調試CAS源碼,一步步跟蹤,一步步查看方法定義。不過讓人遺憾的是,不知道是我的代碼能力有問題,還是因為英文水平太差,或者是下載的CAS源碼版本太久,最終都以失敗告終。在CAS源碼裡面沒有查詢到任何有幫助的代碼,我只是想知道如何判斷當前客戶端已經完成了與CAS的認證、同時獲取到用戶賬號,有這麼難嗎?後來真的都要放棄了。

然後,我也不知道是什麼原因,或者是說靈光一閃也好,還是我天賦異稟也好,哈哈,我突然又把目光轉向了上面那段代碼。抱著試試看的心態模擬了一下:首先登錄Client1系統,會出現CAS登錄頁面,登錄成功後會進入Client1的首頁,然後再登錄Client2(與Client1集成CAS的代碼完全一樣)系統。

哇塞,讓人意想不到的事發生了,Client2系統竟然不需要再次登錄,直接進入了系統首頁,Client2也直接與CAS認證成功,太神奇了。

這說明了什麼?然後我又反覆去梳理上面代碼示例的邏輯。最終確定了一個事實,過程如下:

  • 當Client1與CAS認證成功後,此時已經在客戶端生產了CAS的用戶憑據信息。
  • 如果此時Client2系統第一次去請求CAS的登錄頁,地址示例為【https://cas.test.cn/cas/login? Service =https://Client2/iPortal/Login_admin.aspx】。CAS服務器端可以檢測到當前客戶端環境中存在CAS的用戶憑據信息
  • 然後CAS服務器端不會將當前頁面請求轉到CAS的登錄頁,而是會跳過這一步驟,同時生成有效的ticket,攜帶ticket直接跳轉到Client2的登錄頁。最終跳轉地址如下:【https://Client2/iPortal/Login_admin.aspx?ticket=ST-5-Sx6eyvj7cPPCfn0pMZ】
  • 後面的過程就和“情形一:3)終端攜帶ticket再次請求Client1”中描述的完全一致了。這樣就實現了多個系統直接與CAS認證集成,實現了多個系統的免登陸。

至此,多個CAS-Client與CAS-Server認證集成過程已經順利完成。

看到這裡,大家是不是有豁然開朗的感覺?真是踏破鐵鞋無覓處,得來全不費功夫。沒想到被CAS源碼折磨的死去活來的時候,上面那一段小小的代碼竟然能實現全部功能。在這次研究摸索的過程中,也理解了一種新的SSO單點思路。希望能和大家一起功能進步。


分享到:


相關文章: