<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    我的Blog我做主^_^

    走向一條通往JAVA的不歸路...

      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      64 隨筆 :: 68 文章 :: 77 評(píng)論 :: 0 Trackbacks
    摘要 :?jiǎn)吸c(diǎn)登錄( SSO )的技術(shù)被越來(lái)越廣泛地運(yùn)用到各個(gè)領(lǐng)域的軟件系統(tǒng)當(dāng)中。本文從業(yè)務(wù)的角度分析了單點(diǎn)登錄的需求和應(yīng)用領(lǐng)域;從技術(shù)本身的角度分析了單點(diǎn)登錄技術(shù)的內(nèi)部機(jī)制和實(shí)現(xiàn)手段,并且給出 Web-SSO 和桌面 SSO 的實(shí)現(xiàn)、源代碼和詳細(xì)講解;還從安全和性能的角度對(duì)現(xiàn)有的實(shí)現(xiàn)技術(shù)進(jìn)行進(jìn)一步分析,指出相應(yīng)的風(fēng)險(xiǎn)和需要改進(jìn)的方面。本文除了從多個(gè)方面和角度給出了對(duì)單點(diǎn)登錄( SSO )的全面分析,還并且討論了如何將現(xiàn)有的應(yīng)用和 SSO 服務(wù)結(jié)合起來(lái),能夠幫助應(yīng)用架構(gòu)師和系統(tǒng)分析人員從本質(zhì)上認(rèn)識(shí)單點(diǎn)登錄,從而更好地設(shè)計(jì)出符合需要的安全架構(gòu)。
    關(guān)鍵字 SSO, Java, J2EE, JAAS
    1 什么是單點(diǎn)登陸
    單點(diǎn)登錄( Single Sign On ),簡(jiǎn)稱為 SSO ,是目前比較流行的企業(yè)業(yè)務(wù)整合的解決方案之一。 SSO 的定義是在多個(gè)應(yīng)用系統(tǒng)中,用戶只需要登錄一次就可以訪問(wèn)所有相互信任的應(yīng)用系統(tǒng)。
    較大的企業(yè)內(nèi)部,一般都有很多的業(yè)務(wù)支持系統(tǒng)為其提供相應(yīng)的管理和 IT 服務(wù)。例如財(cái)務(wù)系統(tǒng)為財(cái)務(wù)人員提供財(cái)務(wù)的管理、計(jì)算和報(bào)表服務(wù);人事系統(tǒng)為人事部門(mén)提供全公司人員的維護(hù)服務(wù);各種業(yè)務(wù)系統(tǒng)為公司內(nèi)部不同的業(yè)務(wù)提供不同的服務(wù)等等。這些系統(tǒng)的目的都是讓計(jì)算機(jī)來(lái)進(jìn)行復(fù)雜繁瑣的計(jì)算工作,來(lái)替代人力的手工勞動(dòng),提高工作效率和質(zhì)量。這些不同的系統(tǒng)往往是在不同的時(shí)期建設(shè)起來(lái)的,運(yùn)行在不同的平臺(tái)上;也許是由不同廠商開(kāi)發(fā),使用了各種不同的技術(shù)和標(biāo)準(zhǔn)。如果舉例說(shuō)國(guó)內(nèi)一著名的 IT 公司(名字隱去),內(nèi)部共有 60 多個(gè)業(yè)務(wù)系統(tǒng),這些系統(tǒng)包括兩個(gè)不同版本的 SAP ERP 系統(tǒng), 12 個(gè)不同類(lèi)型和版本的數(shù)據(jù)庫(kù)系統(tǒng), 8 個(gè)不同類(lèi)型和版本的操作系統(tǒng),以及使用了 3 種不同的防火墻技術(shù),還有數(shù)十種互相不能兼容的協(xié)議和標(biāo)準(zhǔn),你相信嗎?不要懷疑,這種情況其實(shí)非常普遍。每一個(gè)應(yīng)用系統(tǒng)在運(yùn)行了數(shù)年以后,都會(huì)成為不可替換的企業(yè) IT 架構(gòu)的一部分,如下圖所示。
    隨著企業(yè)的發(fā)展,業(yè)務(wù)系統(tǒng)的數(shù)量在不斷的增加,老的系統(tǒng)卻不能輕易的替換,這會(huì)帶來(lái)很多的開(kāi)銷(xiāo)。其一是管理上的開(kāi)銷(xiāo),需要維護(hù)的系統(tǒng)越來(lái)越多。很多系統(tǒng)的數(shù)據(jù)是相互冗余和重復(fù)的,數(shù)據(jù)的不一致性會(huì)給管理工作帶來(lái)很大的壓力。業(yè)務(wù)和業(yè)務(wù)之間的相關(guān)性也越來(lái)越大,例如公司的計(jì)費(fèi)系統(tǒng)和財(cái)務(wù)系統(tǒng),財(cái)務(wù)系統(tǒng)和人事系統(tǒng)之間都不可避免的有著密切的關(guān)系。
    為了降低管理的消耗,最大限度的重用已有投資的系統(tǒng),很多企業(yè)都在進(jìn)行著企業(yè)應(yīng)用集成( EAI )。企業(yè)應(yīng)用集成可以在不同層面上進(jìn)行:例如在數(shù)據(jù)存儲(chǔ)層面上的“數(shù)據(jù)大集中”,在傳輸層面上的“通用數(shù)據(jù)交換平臺(tái)”,在應(yīng)用層面上的“業(yè)務(wù)流程整合”,和用戶界面上的“通用企業(yè)門(mén)戶”等等。事實(shí)上,還用一個(gè)層面上的集成變得越來(lái)越重要,那就是“身份認(rèn)證”的整合,也就是“單點(diǎn)登錄”。
    通常來(lái)說(shuō),每個(gè)單獨(dú)的系統(tǒng)都會(huì)有自己的安全體系和身份認(rèn)證系統(tǒng)。整合以前,進(jìn)入每個(gè)系統(tǒng)都需要進(jìn)行登錄,這樣的局面不僅給管理上帶來(lái)了很大的困難,在安全方面也埋下了重大的隱患。下面是一些著名的調(diào)查公司顯示的統(tǒng)計(jì)數(shù)據(jù):
    • 用戶每天平均 16 分鐘花在身份驗(yàn)證任務(wù)上 - 資料來(lái)源: IDS
    • 頻繁的 IT 用戶平均有 21 個(gè)密碼 - 資料來(lái)源: NTA Monitor Password Survey
    • 49% 的人寫(xiě)下了其密碼,而 67% 的人很少改變它們
    • 79 秒出現(xiàn)一起身份被竊事件 - 資料來(lái)源:National Small Business Travel Assoc
    • 全球欺騙損失每年約 12B - 資料來(lái)源:Comm Fraud Control Assoc
    • 2007 年,身份管理市場(chǎng)將成倍增長(zhǎng)至 $4.5B - 資料來(lái)源:IDS
    ?
    使用“單點(diǎn)登錄”整合后,只需要登錄一次就可以進(jìn)入多個(gè)系統(tǒng),而不需要重新登錄,這不僅僅帶來(lái)了更好的用戶體驗(yàn),更重要的是降低了安全的風(fēng)險(xiǎn)和管理的消耗。請(qǐng)看下面的統(tǒng)計(jì)數(shù)據(jù):
    • 提高 IT 效率:對(duì)于每 1000 個(gè)受管用戶,每用戶可節(jié)省$70K
    • 幫助臺(tái)呼叫減少至少1/3,對(duì)于 10K 員工的公司,每年可以節(jié)省每用戶 $75,或者合計(jì) $648K
    • 生產(chǎn)力提高:每個(gè)新員工可節(jié)省 $1K,每個(gè)老員工可節(jié)省 $350 ? 資料來(lái)源:Giga
    • ROI 回報(bào):7.5 13 個(gè)月 ? 資料來(lái)源:Gartner
    ?
    另外,使用“單點(diǎn)登錄”還是 SOA 時(shí)代的需求之一。在面向服務(wù)的架構(gòu)中,服務(wù)和服務(wù)之間,程序和程序之間的通訊大量存在,服務(wù)之間的安全認(rèn)證是 SOA 應(yīng)用的難點(diǎn)之一,應(yīng)此建立“單點(diǎn)登錄”的系統(tǒng)體系能夠大大簡(jiǎn)化 SOA 的安全問(wèn)題,提高服務(wù)之間的合作效率。
    2 單點(diǎn)登陸的技術(shù)實(shí)現(xiàn)機(jī)制
    隨著 SSO 技術(shù)的流行, SSO 的產(chǎn)品也是滿天飛揚(yáng)。所有著名的軟件廠商都提供了相應(yīng)的解決方案。在這里我并不想介紹自己公司( Sun Microsystems )的產(chǎn)品,而是對(duì) SSO 技術(shù)本身進(jìn)行解析,并且提供自己開(kāi)發(fā)這一類(lèi)產(chǎn)品的方法和簡(jiǎn)單演示。有關(guān)我寫(xiě)這篇文章的目的,請(qǐng)參考我的博客( http://yuwang881.blog.sohu.com/3184816.html )。
    單點(diǎn)登錄的機(jī)制其實(shí)是比較簡(jiǎn)單的,用一個(gè)現(xiàn)實(shí)中的例子做比較。頤和園是北京著名的旅游景點(diǎn),也是我常去的地方。在頤和園內(nèi)部有許多獨(dú)立的景點(diǎn),例如“蘇州街”、“佛香閣”和“德和園”,都可以在各個(gè)景點(diǎn)門(mén)口單獨(dú)買(mǎi)票。很多游客需要游玩所有德景點(diǎn),這種買(mǎi)票方式很不方便,需要在每個(gè)景點(diǎn)門(mén)口排隊(duì)買(mǎi)票,錢(qián)包拿進(jìn)拿出的,容易丟失,很不安全。于是絕大多數(shù)游客選擇在大門(mén)口買(mǎi)一張通票(也叫套票),就可以玩遍所有的景點(diǎn)而不需要重新再買(mǎi)票。他們只需要在每個(gè)景點(diǎn)門(mén)口出示一下剛才買(mǎi)的套票就能夠被允許進(jìn)入每個(gè)獨(dú)立的景點(diǎn)。
    單點(diǎn)登錄的機(jī)制也一樣,如下圖所示,當(dāng)用戶第一次訪問(wèn)應(yīng)用系統(tǒng) 1 的時(shí)候,因?yàn)檫€沒(méi)有登錄,會(huì)被引導(dǎo)到認(rèn)證系統(tǒng)中進(jìn)行登錄( 1 );根據(jù)用戶提供的登錄信息,認(rèn)證系統(tǒng)進(jìn)行身份效驗(yàn),如果通過(guò)效驗(yàn),應(yīng)該返回給用戶一個(gè)認(rèn)證的憑據(jù)-- ticket 2 );用戶再訪問(wèn)別的應(yīng)用的時(shí)候( 3 5 )就會(huì)將這個(gè) ticket 帶上,作為自己認(rèn)證的憑據(jù),應(yīng)用系統(tǒng)接受到請(qǐng)求之后會(huì)把 ticket 送到認(rèn)證系統(tǒng)進(jìn)行效驗(yàn),檢查 ticket 的合法性( 4 6 )。如果通過(guò)效驗(yàn),用戶就可以在不用再次登錄的情況下訪問(wèn)應(yīng)用系統(tǒng) 2 和應(yīng)用系統(tǒng) 3 了。
    從上面的視圖可以看出,要實(shí)現(xiàn) SSO ,需要以下主要的功能:
    • 所有應(yīng)用系統(tǒng)共享一個(gè)身份認(rèn)證系統(tǒng)。
      統(tǒng)一的認(rèn)證系統(tǒng)是 SSO 的前提之一。認(rèn)證系統(tǒng)的主要功能是將用戶的登錄信息和用戶信息庫(kù)相比較,對(duì)用戶進(jìn)行登錄認(rèn)證;認(rèn)證成功后,認(rèn)證系統(tǒng)應(yīng)該生成統(tǒng)一的認(rèn)證標(biāo)志( ticket ),返還給用戶。另外,認(rèn)證系統(tǒng)還應(yīng)該對(duì) ticket 進(jìn)行效驗(yàn),判斷其有效性。
    • 所有應(yīng)用系統(tǒng)能夠識(shí)別和提取 ticket 信息
      要實(shí)現(xiàn) SSO 的功能,讓用戶只登錄一次,就必須讓?xiě)?yīng)用系統(tǒng)能夠識(shí)別已經(jīng)登錄過(guò)的用戶。應(yīng)用系統(tǒng)應(yīng)該能對(duì) ticket 進(jìn)行識(shí)別和提取,通過(guò)與認(rèn)證系統(tǒng)的通訊,能自動(dòng)判斷當(dāng)前用戶是否登錄過(guò),從而完成單點(diǎn)登錄的功能。
    ?
    上面的功能只是一個(gè)非常簡(jiǎn)單的 SSO 架構(gòu),在現(xiàn)實(shí)情況下的 SSO 有著更加復(fù)雜的結(jié)構(gòu)。有兩點(diǎn)需要指出的是:
    • 單一的用戶信息數(shù)據(jù)庫(kù)并不是必須的,有許多系統(tǒng)不能將所有的用戶信息都集中存儲(chǔ),應(yīng)該允許用戶信息放置在不同的存儲(chǔ)中,如下圖所示。事實(shí)上,只要統(tǒng)一認(rèn)證系統(tǒng),統(tǒng)一 ticket 的產(chǎn)生和效驗(yàn),無(wú)論用戶信息存儲(chǔ)在什么地方,都能實(shí)現(xiàn)單點(diǎn)登錄。
    ?
    • 統(tǒng)一的認(rèn)證系統(tǒng)并不是說(shuō)只有單個(gè)的認(rèn)證服務(wù)器,如下圖所示,整個(gè)系統(tǒng)可以存在兩個(gè)以上的認(rèn)證服務(wù)器,這些服務(wù)器甚至可以是不同的產(chǎn)品。認(rèn)證服務(wù)器之間要通過(guò)標(biāo)準(zhǔn)的通訊協(xié)議,互相交換認(rèn)證信息,就能完成更高級(jí)別的單點(diǎn)登錄。如下圖,當(dāng)用戶在訪問(wèn)應(yīng)用系統(tǒng) 1 時(shí),由第一個(gè)認(rèn)證服務(wù)器進(jìn)行認(rèn)證后,得到由此服務(wù)器產(chǎn)生的 ticket 。當(dāng)他訪問(wèn)應(yīng)用系統(tǒng) 4 的時(shí)候,認(rèn)證服務(wù)器 2 能夠識(shí)別此 ticket 是由第一個(gè)服務(wù)器產(chǎn)生的,通過(guò)認(rèn)證服務(wù)器之間標(biāo)準(zhǔn)的通訊協(xié)議(例如 SAML )來(lái)交換認(rèn)證信息,仍然能夠完成 SSO 的功能。
    ?
    3 WEB-SSO 的實(shí)現(xiàn)
    隨著互聯(lián)網(wǎng)的高速發(fā)展, WEB 應(yīng)用幾乎統(tǒng)治了絕大部分的軟件應(yīng)用系統(tǒng),因此 WEB-SSO SSO 應(yīng)用當(dāng)中最為流行。 WEB-SSO 有其自身的特點(diǎn)和優(yōu)勢(shì),實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單易用。很多商業(yè)軟件和開(kāi)源軟件都有對(duì) WEB-SSO 的實(shí)現(xiàn)。其中值得一提的是 OpenSSO https://opensso.dev.java.net/ ),為用 Java 實(shí)現(xiàn) WEB-SSO 提供架構(gòu)指南和服務(wù)指南,為用戶自己來(lái)實(shí)現(xiàn) WEB-SSO 提供了理論的依據(jù)和實(shí)現(xiàn)的方法。
    為什么說(shuō) WEB-SSO 比較容易實(shí)現(xiàn)呢?這是有 WEB 應(yīng)用自身的特點(diǎn)決定的。
    眾所周知, Web 協(xié)議(也就是 HTTP )是一個(gè)無(wú)狀態(tài)的協(xié)議。一個(gè) Web 應(yīng)用由很多個(gè) Web 頁(yè)面組成,每個(gè)頁(yè)面都有唯一的 URL 來(lái)定義。用戶在瀏覽器的地址欄輸入頁(yè)面的 URL ,瀏覽器就會(huì)向 Web Server 去發(fā)送請(qǐng)求。如下圖,瀏覽器向 Web 服務(wù)器發(fā)送了兩個(gè)請(qǐng)求,申請(qǐng)了兩個(gè)頁(yè)面。這兩個(gè)頁(yè)面的請(qǐng)求是分別使用了兩個(gè)單獨(dú)的 HTTP 連接。所謂無(wú)狀態(tài)的協(xié)議也就是表現(xiàn)在這里,瀏覽器和 Web 服務(wù)器會(huì)在第一個(gè)請(qǐng)求完成以后關(guān)閉連接通道,在第二個(gè)請(qǐng)求的時(shí)候重新建立連接。 Web 服務(wù)器并不區(qū)分哪個(gè)請(qǐng)求來(lái)自哪個(gè)客戶端,對(duì)所有的請(qǐng)求都一視同仁,都是單獨(dú)的連接。這樣的方式大大區(qū)別于傳統(tǒng)的( Client/Server C/S 結(jié)構(gòu) , 在那樣的應(yīng)用中,客戶端和服務(wù)器端會(huì)建立一個(gè)長(zhǎng)時(shí)間的專(zhuān)用的連接通道。正是因?yàn)橛辛藷o(wú)狀態(tài)的特性,每個(gè)連接資源能夠很快被其他客戶端所重用,一臺(tái) Web 服務(wù)器才能夠同時(shí)服務(wù)于成千上萬(wàn)的客戶端。
    但是我們通常的應(yīng)用是有狀態(tài)的。先不用提不同應(yīng)用之間的 SSO ,在同一個(gè)應(yīng)用中也需要保存用戶的登錄身份信息。例如用戶在訪問(wèn)頁(yè)面 1 的時(shí)候進(jìn)行了登錄,但是剛才也提到,客戶端的每個(gè)請(qǐng)求都是單獨(dú)的連接,當(dāng)客戶再次訪問(wèn)頁(yè)面 2 的時(shí)候,如何才能告訴 Web 服務(wù)器,客戶剛才已經(jīng)登錄過(guò)了呢?瀏覽器和服務(wù)器之間有約定:通過(guò)使用 cookie 技術(shù)來(lái)維護(hù)應(yīng)用的狀態(tài)。 Cookie 是可以被 Web 服務(wù)器設(shè)置的字符串,并且可以保存在瀏覽器中。如下圖所示,當(dāng)瀏覽器訪問(wèn)了頁(yè)面 1 時(shí), web 服務(wù)器設(shè)置了一個(gè) cookie ,并將這個(gè) cookie 和頁(yè)面 1 一起返回給瀏覽器,瀏覽器接到 cookie 之后,就會(huì)保存起來(lái),在它訪問(wèn)頁(yè)面 2 的時(shí)候會(huì)把這個(gè) cookie 也帶上, Web 服務(wù)器接到請(qǐng)求時(shí)也能讀出 cookie 的值,根據(jù) cookie 值的內(nèi)容就可以判斷和恢復(fù)一些用戶的信息狀態(tài)。
    Web-SSO 完全可以利用 Cookie 結(jié)束來(lái)完成用戶登錄信息的保存,將瀏覽器中的 Cookie 和上文中的 Ticket 結(jié)合起來(lái),完成 SSO 的功能。
    ?
    為了完成一個(gè)簡(jiǎn)單的 SSO 的功能,需要兩個(gè)部分的合作:
    1. 統(tǒng)一的身份認(rèn)證服務(wù)。
    2. 修改 Web 應(yīng)用,使得每個(gè)應(yīng)用都通過(guò)這個(gè)統(tǒng)一的認(rèn)證服務(wù)來(lái)進(jìn)行身份效驗(yàn)。
    3.1 Web SSO 的樣例
    根據(jù)上面的原理,我用 J2EE 的技術(shù)( JSP Servlet )完成了一個(gè)具有 Web-SSO 的簡(jiǎn)單樣例。樣例包含一個(gè)身份認(rèn)證的服務(wù)器和兩個(gè)簡(jiǎn)單的 Web 應(yīng)用,使得這兩個(gè) Web 應(yīng)用通過(guò)統(tǒng)一的身份認(rèn)證服務(wù)來(lái)完成 Web-SSO 的功能。此樣例所有的源代碼和二進(jìn)制代碼都可以從網(wǎng)站地址 http://gceclub.sun.com.cn/wangyu/ 下載。
    ?
    樣例下載、安裝部署和運(yùn)行指南:
    • Web-SSO 的樣例是由三個(gè)標(biāo)準(zhǔn) Web 應(yīng)用組成,壓縮成三個(gè) zip 文件,從 http://gceclub.sun.com.cn/wangyu// 中下載。其中 SSOAuth http://gceclub.sun.com.cn/wangyu/ )是身份認(rèn)證服務(wù); SSOWebDemo1 http://gceclub.sun.com.cn/wangyu/ )和 SSOWebDemo2 http://gceclub.sun.com.cn/wangyu/ )是兩個(gè)用來(lái)演示單點(diǎn)登錄的 Web 應(yīng)用。這三個(gè) Web 應(yīng)用之所以沒(méi)有打成 war 包,是因?yàn)樗鼈儾荒苤苯硬渴穑鶕?jù)讀者的部署環(huán)境需要作出小小的修改。樣例部署和運(yùn)行的環(huán)境有一定的要求,需要符合 Servlet2.3 以上標(biāo)準(zhǔn)的 J2EE 容器才能運(yùn)行(例如 Tomcat5,Sun Application Server 8, Jboss 4 等)。另外,身份認(rèn)證服務(wù)需要 JDK1.5 的運(yùn)行環(huán)境。之所以要用 JDK1.5 是因?yàn)楣P者使用了一個(gè)線程安全的高性能的 Java 集合類(lèi)“ ConcurrentMap” ,只有在 JDK1.5 中才有。
    • 這三個(gè) Web 應(yīng)用完全可以單獨(dú)部署,它們可以分別部署在不同的機(jī)器,不同的操作系統(tǒng)和不同的 J2EE 的產(chǎn)品上,它們完全是標(biāo)準(zhǔn)的和平臺(tái)無(wú)關(guān)的應(yīng)用。但是有一個(gè)限制,那兩臺(tái)部署應(yīng)用( demo1 demo2 )的機(jī)器的域名需要相同,這在后面的章節(jié)中會(huì)解釋到 cookie domain 的關(guān)系以及如何制作跨域的 WEB-SSO
    • 解壓縮 SSOAuth.zip 文件,在 /WEB-INF/ 下的 web.xml 中請(qǐng)修改“ domainname” 的屬性以反映實(shí)際的應(yīng)用部署情況, domainname 需要設(shè)置為兩個(gè)單點(diǎn)登錄的應(yīng)用( demo1 demo2 )所屬的域名。這個(gè) domainname 和當(dāng)前 SSOAuth 服務(wù)部署的機(jī)器的域名沒(méi)有關(guān)系。我缺省設(shè)置的是“ .sun.com” 。如果你部署 demo1 demo2 的機(jī)器沒(méi)有域名,請(qǐng)輸入 IP 地址或主機(jī)名(如 localhost ),但是如果使用 IP 地址或主機(jī)名也就意味著 demo1 demo2 需要部署到一臺(tái)機(jī)器上了。設(shè)置完后,根據(jù)你所選擇的 J2EE 容器,可能需要將 SSOAuth 這個(gè)目錄壓縮打包成 war 文件。用“ jar -cvf SSOAuth.war SSOAuth/” 就可以完成這個(gè)功能。
    • 解壓縮 SSOWebDemo1 SSOWebDemo2 文件,分別在它們 /WEB-INF/ 下找到 web.xml 文件,請(qǐng)修改其中的幾個(gè)初始化參數(shù)
      <init-param>
      <param-name>SSOServiceURL</param-name>
      <param-value>
      http://wangyu.prc.sun.com:8080/SSOAuth/SSOAuth </param-value>
      </init-param>
      <init-param>
      <param-name>SSOLoginPage</param-name>
      <param-value>
      http://wangyu.prc.sun.com:8080/SSOAuth/login.jsp </param-value>
      </init-param>
      將其中的 SSOServiceURL SSOLoginPage 修改成部署 SSOAuth 應(yīng)用的機(jī)器名、端口號(hào)以及根路徑(缺省是 SSOAuth )以反映實(shí)際的部署情況。設(shè)置完后,根據(jù)你所選擇的 J2EE 容器,可能需要將 SSOWebDemo1 SSOWebDemo2 這兩個(gè)目錄壓縮打包成兩個(gè) war 文件。用“ jar -cvf SSOWebDemo1.war SSOWebDemo1/” 就可以完成這個(gè)功能。
    • 請(qǐng)輸入第一個(gè) web 應(yīng)用的測(cè)試 URL test.jsp , 例如 http://wangyu.prc.sun.com:8080/ SSOWebDemo1/test.jsp ,如果是第一次訪問(wèn),便會(huì)自動(dòng)跳轉(zhuǎn)到登錄界面,如下圖

    • 使用系統(tǒng)自帶的三個(gè)帳號(hào)之一登錄(例如,用戶名: wangyu, 密碼: wangyu ),便能成功的看到 test.jsp 的內(nèi)容:顯示當(dāng)前用戶名和歡迎信息。
    • 請(qǐng)接著在同一個(gè)瀏覽器中輸入第二個(gè) web 應(yīng)用的測(cè)試 URL test.jsp , 例如 http://wangyu.prc.sun.com:8080/ SSOWebDemo2/test.jsp 。你會(huì)發(fā)現(xiàn),不需要再次登錄就能看到 test.jsp 的內(nèi)容,同樣是顯示當(dāng)前用戶名和歡迎信息,而且歡迎信息中明確的顯示當(dāng)前的應(yīng)用名稱( demo2 )。
    ?????????????
    3.2 WEB-SSO 代碼講解
    3.2.1 身份認(rèn)證服務(wù)代碼解析
    Web-SSO 的源代碼可以從網(wǎng)站地址 http://gceclub.sun.com.cn/wangyu/web-sso/websso_src.zip 下載。身份認(rèn)證服務(wù)是一個(gè)標(biāo)準(zhǔn)的 web 應(yīng)用,包括一個(gè)名為 SSOAuth Servlet ,一個(gè) login.jsp 文件和一個(gè) failed.html 。身份認(rèn)證的所有服務(wù)幾乎都由 SSOAuth Servlet 來(lái)實(shí)現(xiàn)了; login.jsp 用來(lái)顯示登錄的頁(yè)面(如果發(fā)現(xiàn)用戶還沒(méi)有登錄過(guò)); failed.html 是用來(lái)顯示登錄失敗的信息(如果用戶的用戶名和密碼與信息數(shù)據(jù)庫(kù)中的不一樣)。
    SSOAuth 的代碼如下面的列表顯示,結(jié)構(gòu)非常簡(jiǎn)單,先看看這個(gè) Servlet 的主體部分
    package DesktopSSO;
    ?
    import java.io.*;
    import java.net.*;
    import java.text.*;
    import java.util.*;
    import java.util.concurrent.*;
    ?
    import javax.servlet.*;
    import javax.servlet.http.*;
    ?
    ?
    public class SSOAuth extends HttpServlet {
    ???
    ??? static private ConcurrentMap accounts;
    ??? static private ConcurrentMap SSOIDs;
    ??? String cookiename="WangYuDesktopSSOID";
    ??? String domainname;
    ???
    ??? public void init(ServletConfig config) throws ServletException {
    ??????? super.init(config);
    ??????? domainname= config.getInitParameter("domainname");
    ??????? cookiename = config.getInitParameter("cookiename");
    ??????? SSOIDs = new ConcurrentHashMap();
    ??????? accounts=new ConcurrentHashMap();
    ??????? accounts.put("wangyu", "wangyu");
    ??????? accounts.put("paul", "paul");
    ??????? accounts.put("carol", "carol");
    ??? }
    ?
    ??? protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ??????? PrintWriter out = response.getWriter();
    ??????? String action = request.getParameter("action");
    ??????? String result="failed";
    ??????? if (action==null) {
    ??????????? handlerFromLogin(request,response);
    ??????? } else if (action.equals("authcookie")){
    ??????????? String myCookie = request.getParameter("cookiename");
    ??????????? if (myCookie != null)?result = authCookie(myCookie);
    ??????????? out.print(result);
    ??????????? out.close();
    ??????? } else if (action.equals("authuser")) {
    ??????????? result=authNameAndPasswd(request,response);
    ??????????? out.print(result);
    ??????????? out.close();
    ??????? }?else if (action.equals("logout")) {
    ??????????? String myCookie = request.getParameter("cookiename");
    ??????????? logout(myCookie);
    ?? ????????? out.close();
    ??????? }
    ??? }
    ?
    .....
    ?
    }
    ?
    從代碼很容易看出, SSOAuth 就是一個(gè)簡(jiǎn)單的 Servlet 。其中有兩個(gè)靜態(tài)成員變量: accounts SSOIDs ,這兩個(gè)成員變量都使用了 JDK1.5 中線程安全的 MAP 類(lèi): ConcurrentMap ,所以這個(gè)樣例一定要 JDK1.5 才能運(yùn)行。 Accounts 用來(lái)存放用戶的用戶名和密碼,在 init() 的方法中可以看到我給系統(tǒng)添加了三個(gè)合法的用戶。在實(shí)際應(yīng)用中, accounts 應(yīng)該是去數(shù)據(jù)庫(kù)中或 LDAP 中獲得,為了簡(jiǎn)單起見(jiàn),在本樣例中我使用了 ConcurrentMap 在內(nèi)存中用程序創(chuàng)建了三個(gè)用戶。而 SSOIDs 保存了在用戶成功的登錄后所產(chǎn)生的 cookie 和用戶名的對(duì)應(yīng)關(guān)系。它的功能顯而易見(jiàn):當(dāng)用戶成功登錄以后,再次訪問(wèn)別的系統(tǒng),為了鑒別這個(gè)用戶請(qǐng)求所帶的 cookie 的有效性,需要到 SSOIDs 中檢查這樣的映射關(guān)系是否存在。
    ?
    在主要的請(qǐng)求處理方法 processRequest() 中,可以很清楚的看到 SSOAuth 的所有功能
    1. 如果用戶還沒(méi)有登錄過(guò),是第一次登錄本系統(tǒng),會(huì)被跳轉(zhuǎn)到 login.jsp 頁(yè)面(在后面會(huì)解釋如何跳轉(zhuǎn))。用戶在提供了用戶名和密碼以后,就會(huì)用 handlerFromLogin() 這個(gè)方法來(lái)驗(yàn)證。
    2. 如果用戶已經(jīng)登錄過(guò)本系統(tǒng),再訪問(wèn)別的應(yīng)用的時(shí)候,是不需要再次登錄的。因?yàn)闉g覽器會(huì)將第一次登錄時(shí)產(chǎn)生的 cookie 和請(qǐng)求一起發(fā)送。效驗(yàn) cookie 的有效性是 SSOAuth 的主要功能之一。
    3. SSOAuth 還能直接效驗(yàn)非 login.jsp 頁(yè)面過(guò)來(lái)的用戶名和密碼的效驗(yàn)請(qǐng)求。這個(gè)功能是用于非 web 應(yīng)用的 SSO ,這在后面的桌面 SSO 中會(huì)用到。
    4. SSOAuth 還提供 logout 服務(wù)。
    ?
    下面看看幾個(gè)主要的功能函數(shù):
    ? private void handlerFromLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ??????? String username = request.getParameter("username");
    ??????? String password = request.getParameter("password");
    ??????? String pass = (String)accounts.get(username);
    ??????? if ((pass==null)||(!pass.equals(password)))
    ??????????? getServletContext().getRequestDispatcher("/failed.html").forward(request, response);
    ??????? else {
    ??????????? String gotoURL = request.getParameter("goto");
    ??????????? String newID = createUID();
    ??????????? SSOIDs.put(newID, username);
    ??????????? Cookie wangyu = new Cookie(cookiename, newID);
    ??????????? wangyu.setDomain(domainname);
    ??????????? wangyu.setMaxAge(60000);
    ??????????? wangyu.setValue(newID);
    ??????????? wangyu.setPath("/");
    ??????????? response.addCookie(wangyu);
    ??????????? System.out.println("login success, goto back url:" + gotoURL);
    ??????????? if (gotoURL != null) {
    ??????????????? PrintWriter out = response.getWriter();
    ???? ??????????? response.sendRedirect(gotoURL);
    ??????????????? out.close();
    ??????????? }
    ??????? }??
    ??? }
    handlerFromLogin() 這個(gè)方法是用來(lái)處理來(lái)自 login.jsp 的登錄請(qǐng)求。它的邏輯很簡(jiǎn)單:將用戶輸入的用戶名和密碼與預(yù)先設(shè)定好的用戶集合(存放在 accounts 中)相比較,如果用戶名或密碼不匹配的話,則返回登錄失敗的頁(yè)面( failed.html ),如果登錄成功的話,需要為用戶當(dāng)前的 session 創(chuàng)建一個(gè)新的 ID ,并將這個(gè) ID 和用戶名的映射關(guān)系存放到 SSOIDs 中,最后還要將這個(gè) ID 設(shè)置為瀏覽器能夠保存的 cookie 值。
    登錄成功后,瀏覽器會(huì)到哪個(gè)頁(yè)面呢?那我們回顧一下我們是如何使用身份認(rèn)證服務(wù)的。一般來(lái)說(shuō)我們不會(huì)直接訪問(wèn)身份服務(wù)的任何 URL ,包括 login.jsp 。身份服務(wù)是用來(lái)保護(hù)其他應(yīng)用服務(wù)的,用戶一般在訪問(wèn)一個(gè)受 SSOAuth 保護(hù)的 Web 應(yīng)用的某個(gè) URL 時(shí),當(dāng)前這個(gè)應(yīng)用會(huì)發(fā)現(xiàn)當(dāng)前的用戶還沒(méi)有登錄,便強(qiáng)制將也頁(yè)面轉(zhuǎn)向 SSOAuth login.jsp ,讓用戶登錄。如果登錄成功后,應(yīng)該自動(dòng)的將用戶的瀏覽器指向用戶最初想訪問(wèn)的那個(gè) URL 。在 handlerFromLogin() 這個(gè)方法中,我們通過(guò)接收 goto” 這個(gè)參數(shù)來(lái)保存用戶最初訪問(wèn)的 URL ,成功后便重新定向到這個(gè)頁(yè)面中。
    另外一個(gè)要說(shuō)明的是,在設(shè)置 cookie 的時(shí)候,我使用了一個(gè)setMaxAge(6000) 的方法。這個(gè)方法是用來(lái)設(shè)置 cookie 的有效期,單位是秒。如果不使用這個(gè)方法或者參數(shù)為負(fù)數(shù)的話,當(dāng)瀏覽器關(guān)閉的時(shí)候,這個(gè) cookie 就失效了。在這里我給了很大的值( 1000 分鐘),導(dǎo)致的行為是:當(dāng)你關(guān)閉瀏覽器(或者關(guān)機(jī)),下次再打開(kāi)瀏覽器訪問(wèn)剛才的應(yīng)用,只要在 1000 分鐘之內(nèi),就不需要再登錄了。我這樣做是下面要介紹的桌面 SSO 中所需要的功能。
    其他的方法更加簡(jiǎn)單,這里就不多解釋了。
    ?
    3.2.2 具有 SSO 功能的 web 應(yīng)用源代碼解析
    要實(shí)現(xiàn) WEB-SSO 的功能,只有身份認(rèn)證服務(wù)是不夠的。這點(diǎn)很顯然,要想使多個(gè)應(yīng)用具有單點(diǎn)登錄的功能,還需要每個(gè)應(yīng)用本身的配合:將自己的身份認(rèn)證的服務(wù)交給一個(gè)統(tǒng)一的身份認(rèn)證服務(wù)- SSOAuth SSOAuth 服務(wù)中提供的各個(gè)方法就是供每個(gè)加入 SSO Web 應(yīng)用來(lái)調(diào)用的。
    一般來(lái)說(shuō), Web 應(yīng)用需要 SSO 的功能,應(yīng)該通過(guò)以下的交互過(guò)程來(lái)調(diào)用身份認(rèn)證服務(wù)的提供的認(rèn)證服務(wù):
    • Web 應(yīng)用中每一個(gè)需要安全保護(hù)的 URL 在訪問(wèn)以前,都需要進(jìn)行安全檢查,如果發(fā)現(xiàn)沒(méi)有登錄(沒(méi)有發(fā)現(xiàn)認(rèn)證之后所帶的 cookie ),就重新定向到 SSOAuth 中的 login.jsp 進(jìn)行登錄。
    • 登錄成功后,系統(tǒng)會(huì)自動(dòng)給你的瀏覽器設(shè)置 cookie ,證明你已經(jīng)登錄過(guò)了。
    • 當(dāng)你再訪問(wèn)這個(gè)應(yīng)用的需要保護(hù)的 URL 的時(shí)候,系統(tǒng)還是要進(jìn)行安全檢查的,但是這次系統(tǒng)能夠發(fā)現(xiàn)相應(yīng)的 cookie
    • 有了這個(gè) cookie ,還不能證明你就一定有權(quán)限訪問(wèn)。因?yàn)橛锌赡苣阋呀?jīng) logout, 或者 cookie 已經(jīng)過(guò)期了,或者身份認(rèn)證服務(wù)重起過(guò),這些情況下,你的 cookie 都可能無(wú)效。應(yīng)用系統(tǒng)拿到這個(gè) cookie ,還需要調(diào)用身份認(rèn)證的服務(wù),來(lái)判斷 cookie 時(shí)候真的有效,以及當(dāng)前的 cookie 對(duì)應(yīng)的用戶是誰(shuí)。
    • 如果 cookie 效驗(yàn)成功,就允許用戶訪問(wèn)當(dāng)前請(qǐng)求的資源。
    以上這些功能,可以用很多方法來(lái)實(shí)現(xiàn):
    • 在每個(gè)被訪問(wèn)的資源中( JSP Servlet )中都加入身份認(rèn)證的服務(wù),來(lái)獲得 cookie ,并且判斷當(dāng)前用戶是否登錄過(guò)。不過(guò)這個(gè)笨方法沒(méi)有人會(huì)用 :-)
    • 可以通過(guò)一個(gè) controller ,將所有的功能都寫(xiě)到一個(gè) servlet 中,然后在 URL 映射的時(shí)候,映射到所有需要保護(hù)的 URL 集合中(例如 *.jsp /security/* 等)。這個(gè)方法可以使用,不過(guò),它的缺點(diǎn)是不能重用。在每個(gè)應(yīng)用中都要部署一個(gè)相同的 servlet
    • Filter 是比較好的方法。符合 Servlet2.3 以上的 J2EE 容器就具有部署 filter 的功能。( Filter 的使用可以參考 JavaWolrd 的文章 http://www.javaworld.com/javaworld/jw-06-2001/jw-0622-filters.html Filter 是一個(gè)具有很好的模塊化,可重用的編程 API ,用在 SSO 正合適不過(guò)。本樣例就是使用一個(gè) filter 來(lái)完成以上的功能。
    ?
    package SSO;
    ?
    import java.io.*;
    import java.net.*;
    import java.util.*;
    import java.text.*;
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.*;
    import org.apache.commons.httpclient.*;
    import org.apache.commons.httpclient.methods.GetMethod;
    ?
    public class SSOFilter implements Filter {
    ??? private FilterConfig filterConfig = null;
    ??? private String cookieName="WangYuDesktopSSOID";
    ??? private String SSOServiceURL=?"http://wangyu.prc.sun.com:8080/SSOAuth/SSOAuth";
    ??? private String SSOLoginPage= "http://wangyu.prc.sun.com:8080/SSOAuth/login.jsp";
    ???
    ??? public void init(FilterConfig filterConfig) {
    ?
    ??????? this.filterConfig = filterConfig;
    ??????? if (filterConfig != null) {
    ??????????? if (debug) {
    ??????????????? log("SSOFilter:Initializing filter");
    ??????????? }
    ??????? }???????
    ??????? cookieName = filterConfig.getInitParameter("cookieName");
    ??????? SSOServiceURL = filterConfig.getInitParameter("SSOServiceURL");
    ??????? SSOLoginPage = filterConfig.getInitParameter("SSOLoginPage");
    ??? }?
    .....
    .....
    ?
    }
    以上的初始化的源代碼有兩點(diǎn)需要說(shuō)明:一是有兩個(gè)需要配置的參數(shù) SSOServiceURL SSOLoginPage 。因?yàn)楫?dāng)前的 Web 應(yīng)用很可能和身份認(rèn)證服務(wù)( SSOAuth )不在同一臺(tái)機(jī)器上,所以需要讓這個(gè) filter 知道身份認(rèn)證服務(wù)部署的 URL ,這樣才能去調(diào)用它的服務(wù)。另外一點(diǎn)就是由于身份認(rèn)證的服務(wù)調(diào)用是要通過(guò) http 協(xié)議來(lái)調(diào)用的(在本樣例中是這樣設(shè)計(jì)的,讀者完全可以設(shè)計(jì)自己的身份服務(wù),使用別的調(diào)用協(xié)議,如 RMI SOAP 等等),所有筆者引用了 apache commons 工具包(詳細(xì)信息情訪問(wèn) apache 的網(wǎng)站 http://jakarta.apache.org/commons/index.html ),其中的 httpclient” 可以大大簡(jiǎn)化 http 調(diào)用的編程。
    下面看看 filter 的主體方法 doFilter():
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    ??????? if (debug) log("SSOFilter:doFilter()");
    ??????? HttpServletRequest request = (HttpServletRequest) req;
    ??????? HttpServletResponse response = (HttpServletResponse) res;
    ??????? String result="failed";
    ??????? String url = request.getRequestURL().toString();
    ??????? String qstring = request.getQueryString();
    ??????? if (qstring == null) qstring ="";
    ?
    ??????? // 檢查 http 請(qǐng)求的 head 是否有需要的 cookie
    ??????? String cookieValue ="";
    ??????? javax.servlet.http.Cookie[] diskCookies = request.getCookies();
    ??????? if (diskCookies != null) {
    ??????????? for (int i = 0; i < diskCookies.length; i++) {
    ??????????????? if(diskCookies[i].getName().equals(cookieName)){
    ??????????????????? cookieValue = diskCookies[i].getValue();
    ?
    ??????????????????? // 如果找到了相應(yīng)的 cookie 則效驗(yàn)其有效性
    ??????????????????? result = SSOService(cookieValue);
    ??????????????????? if (debug) log("found cookies!");
    ???????? ??????? }
    ??????????? }
    ??????? }
    ??????? if (result.equals("failed")) { // 效驗(yàn)失敗或沒(méi)有找到 cookie ,則需要登錄
    ??????????? response.sendRedirect(SSOLoginPage+"?goto="+url);
    ??????? } else if (qstring.indexOf("logout") > 1) {//logout 服務(wù)
    ??????????? if (debug) log("logout action!");
    ??????????? logoutService(cookieValue);
    ??????????? response.sendRedirect(SSOLoginPage+"?goto="+url);
    ??????? }?else {// 效驗(yàn)成功
    ??????????? request.setAttribute("SSOUser",result);
    ??????????? Throwable problem = null;
    ??????????? try {
    ??????????? ???? chain.doFilter(req, res);
    ??????????? } catch(Throwable t) {
    ??????????????? problem = t;
    ??????????????? t.printStackTrace();
    ??????????? }??????
    ??????????? if (problem != null) {
    ??????????????? if (problem instanceof ServletException) throw (ServletException)problem;
    ??????????????? if (problem instanceof IOException) throw (IOException)problem;
    ??????????????? sendProcessingError(problem, res);
    ??????????? }
    ??????? }??
    ??? }
    doFilter() 方法的邏輯也是非常簡(jiǎn)單的,在接收到請(qǐng)求的時(shí)候,先去查找是否存在期望的 cookie 值,如果找到了,就會(huì)調(diào)用 SSOService(cookieValue) 去效驗(yàn)這個(gè) cookie 的有效性。如果 cookie 效驗(yàn)不成功或者 cookie 根本不存在,就會(huì)直接轉(zhuǎn)到登錄界面讓用戶登錄;如果 cookie 效驗(yàn)成功,就不會(huì)做任何阻攔,讓此請(qǐng)求進(jìn)行下去。在配置文件中,有下面的一個(gè)節(jié)點(diǎn)表示了此 filter URL 映射關(guān)系:只攔截所有的 jsp 請(qǐng)求。
    <filter-mapping>
    <filter-name>SSOFilter</filter-name>
    <url-pattern>*.jsp</url-pattern>
    </filter-mapping>
    ?
    下面還有幾個(gè)主要的函數(shù)需要說(shuō)明:
    ??? private String SSOService(String cookievalue) throws IOException {
    ??????? String authAction = "?action=authcookie&cookiename=";
    ??????? HttpClient httpclient = new HttpClient();
    ??????? GetMethod httpget = new GetMethod(SSOServiceURL+authAction+cookievalue);
    ??????? try {?
    ??????????? httpclient.executeMethod(httpget);
    ??????????? String result = httpget.getResponseBodyAsString();
    ??????????? return result;
    ??????? } finally {
    ??????????? httpget.releaseConnection();
    ??????? }
    ??? }
    ???
    ??? private void logoutService(String cookievalue) throws IOException {
    ??????? String authAction = "?action=logout&cookiename=";
    ??????? HttpClient httpclient = new HttpClient();
    ??????? GetMethod httpget = new GetMethod(SSOServiceURL+authAction+cookievalue);
    ??????? try {
    ??????????? httpclient.executeMethod(httpget);
    ??????????? httpget.getResponseBodyAsString();
    ??????? } finally {
    ??????????? httpget.releaseConnection();
    ??????? }
    ??? }
    這兩個(gè)函數(shù)主要是利用 apache 中的 httpclient 訪問(wèn) SSOAuth 提供的認(rèn)證服務(wù)來(lái)完成效驗(yàn) cookie logout 的功能。
    其他的函數(shù)都很簡(jiǎn)單,有很多都是我的 IDE NetBeans )替我自動(dòng)生成的。
    4 當(dāng)前方案的安全局限性
    當(dāng)前這個(gè) WEB-SSO 的方案是一個(gè)比較簡(jiǎn)單的雛形,主要是用來(lái)演示 SSO 的概念和說(shuō)明 SSO 技術(shù)的實(shí)現(xiàn)方式。有很多方面還需要完善,其中安全性是非常重要的一個(gè)方面。
    我們說(shuō)過(guò),采用 SSO 技術(shù)的主要目的之一就是加強(qiáng)安全性,降低安全風(fēng)險(xiǎn)。因?yàn)椴捎昧?/span> SSO ,在網(wǎng)絡(luò)上傳遞密碼的次數(shù)減少,風(fēng)險(xiǎn)降低是顯然的,但是當(dāng)前的方案卻有其他的安全風(fēng)險(xiǎn)。由于 cookie 是一個(gè)用戶登錄的唯一憑據(jù),對(duì) cookie 的保護(hù)措施是系統(tǒng)安全的重要環(huán)節(jié):
    • cookie 的長(zhǎng)度和復(fù)雜度
      在本方案中, cookie 是有一個(gè)固定的字符串(我的姓名)加上當(dāng)前的時(shí)間戳。這樣的 cookie 很容易被偽造和猜測(cè)。懷有惡意的用戶如果猜測(cè)到合法的 cookie 就可以被當(dāng)作已經(jīng)登錄的用戶,任意訪問(wèn)權(quán)限范圍內(nèi)的資源
    • cookie 的效驗(yàn)和保護(hù)
      在本方案中,雖然密碼只要傳輸一次就夠了,可 cookie 在網(wǎng)絡(luò)中是經(jīng)常傳來(lái)傳去。一些網(wǎng)絡(luò)探測(cè)工具(如 sniff, snoop,tcpdump 等)可以很容易捕獲到 cookie 的數(shù)值。在本方案中,并沒(méi)有考慮 cookie 在傳輸時(shí)候的保護(hù)。另外對(duì) cookie 的效驗(yàn)也過(guò)于簡(jiǎn)單,并不去檢查發(fā)送 cookie 的來(lái)源究竟是不是 cookie 最初的擁有者,也就是說(shuō)無(wú)法區(qū)分正常的用戶和仿造 cookie 的用戶。
    • 當(dāng)其中一個(gè)應(yīng)用的安全性不好,其他所有的應(yīng)用都會(huì)受到安全威脅
      因?yàn)橛?/span> SSO ,所以當(dāng)某個(gè)處于 SSO 的應(yīng)用被黒客攻破,那么很容易攻破其他處于同一個(gè) SSO 保護(hù)的應(yīng)用。
    這些安全漏洞在商業(yè)的 SSO 解決方案中都會(huì)有所考慮,提供相關(guān)的安全措施和保護(hù)手段,例如 Sun 公司的 Access Manager cookie 的復(fù)雜讀和對(duì) cookie 的保護(hù)都做得非常好。另外在 OpneSSO https://opensso.dev.java.net/ )的架構(gòu)指南中也給出了部分安全措施的解決方案。
    5 當(dāng)前方案的功能和性能局限性
    除了安全性,當(dāng)前方案在功能和性能上都需要很多的改進(jìn):
    • 當(dāng)前所提供的登錄認(rèn)證模式只有一種:用戶名和密碼,而且為了簡(jiǎn)單,將用戶名和密碼放在內(nèi)存當(dāng)中。事實(shí)上,用戶身份信息的來(lái)源應(yīng)該是多種多樣的,可以是來(lái)自數(shù)據(jù)庫(kù)中, LDAP 中,甚至于來(lái)自操作系統(tǒng)自身的用戶列表。還有很多其他的認(rèn)證模式都是商務(wù)應(yīng)用不可缺少的,因此 SSO 的解決方案應(yīng)該包括各種認(rèn)證的模式,包括數(shù)字證書(shū), Radius SafeWord MemberShip SecurID 等多種方式。最為靈活的方式應(yīng)該允許可插入的 JAAS 框架來(lái)擴(kuò)展身份認(rèn)證的接口
    • 我們編寫(xiě)的 Filter 只能用于 J2EE 的應(yīng)用,而對(duì)于大量非 Java Web 應(yīng)用,卻無(wú)法提供 SSO 服務(wù)。
    • 在將 Filter 應(yīng)用到 Web 應(yīng)用的時(shí)候,需要對(duì)容器上的每一個(gè)應(yīng)用都要做相應(yīng)的修改,重新部署。而更加流行的做法是 Agent 機(jī)制:為每一個(gè)應(yīng)用服務(wù)器安裝一個(gè) agent ,就可以將 SSO 功能應(yīng)用到這個(gè)應(yīng)用服務(wù)器中的所有應(yīng)用。
    • 當(dāng)前的方案不能支持分別位于不同 domain Web 應(yīng)用進(jìn)行 SSO 。這是因?yàn)闉g覽器在訪問(wèn) Web 服務(wù)器的時(shí)候,僅僅會(huì)帶上和當(dāng)前 web 服務(wù)器具有相同 domain 名稱的那些 cookie 。要提供跨域的 SSO 的解決方案有很多其他的方法,在這里就不多說(shuō)了。 Sun Access Manager 就具有跨域的 SSO 的功能。
    • 另外, Filter 的性能問(wèn)題也是需要重視的方面。因?yàn)?/span> Filter 會(huì)截獲每一個(gè)符合 URL 映射規(guī)則的請(qǐng)求,獲得 cookie ,驗(yàn)證其有效性。這一系列任務(wù)是比較消耗資源的,特別是驗(yàn)證 cookie 有效性是一個(gè)遠(yuǎn)程的 http 的調(diào)用,來(lái)訪問(wèn) SSOAuth 的認(rèn)證服務(wù),有一定的延時(shí)。因此在性能上需要做進(jìn)一步的提高。例如在本樣例中,如果將 URL 映射從“ .jsp 改成“ /* ,也就是說(shuō) filter 對(duì)所有的請(qǐng)求都起作用,整個(gè)應(yīng)用會(huì)變得非常慢。這是因?yàn)椋?yè)面當(dāng)中包含了各種靜態(tài)元素如 gif 圖片, css 樣式文件,和其他 html 靜態(tài)頁(yè)面,這些頁(yè)面的訪問(wèn)都要通過(guò) filter 去驗(yàn)證。而事實(shí)上,這些靜態(tài)元素沒(méi)有什么安全上的需求,應(yīng)該在 filter 中進(jìn)行判斷,不去效驗(yàn)這些請(qǐng)求,性能會(huì)好很多。另外,如果在 filter 中加上一定的 cache ,而不需要每一個(gè) cookie 效驗(yàn)請(qǐng)求都去遠(yuǎn)端的身份認(rèn)證服務(wù)中執(zhí)行,性能也能大幅度提高。
    • 另外系統(tǒng)還需要很多其他的服務(wù),如在內(nèi)存中定時(shí)刪除無(wú)用的 cookie 映射等等,都是一個(gè)嚴(yán)肅的解決方案需要考慮的問(wèn)題。
    6 桌面 SSO 的實(shí)現(xiàn)
    WEB-SSO 的概念延伸開(kāi),我們可以把 SSO 的技術(shù)拓展到整個(gè)桌面的應(yīng)用,不僅僅局限在瀏覽器。 SSO 的概念和原則都沒(méi)有改變,只需要再做一點(diǎn)點(diǎn)的工作,就可以完成桌面 SSO 的應(yīng)用。
    桌面 SSO WEB-SSO 一樣,關(guān)鍵的技術(shù)也在于如何在用戶登錄過(guò)后保存登錄的憑據(jù)。在 WEB-SSO 中,登錄的憑據(jù)是靠瀏覽器的 cookie 機(jī)制來(lái)完成的;在桌面應(yīng)用中,可以將登錄的憑證保存到任何地方,只要所有 SSO 的桌面應(yīng)用都共享這個(gè)憑證。
    從網(wǎng)站可以下載一個(gè)簡(jiǎn)單的桌面 SSO 的樣例 (http://gceclub.sun.com.cn/wangyu/ 和全部源碼( http://gceclub.sun.com.cn/wangyu/desktop-sso/desktopsso_src.zip ),雖然簡(jiǎn)單,但是它具有桌面 SSO 大多數(shù)的功能,稍微加以擴(kuò)充就可以成為自己的解決方案。
    ?
    6.1 桌面樣例的部署
    1. 運(yùn)行此桌面 SSO 需要三個(gè)前提條件:
      a) WEB-SSO
      的身份認(rèn)證應(yīng)用應(yīng)該正在運(yùn)行,因?yàn)槲覀冊(cè)谧烂?/span> SSO 當(dāng)中需要用到統(tǒng)一的認(rèn)證服務(wù)
      b)
      當(dāng)前桌面需要運(yùn)行 Mozilla Netscape 瀏覽器,因?yàn)槲覀儗?/span> ticket 保存到 mozilla cookie 文件中
      c)
      必須在 JDK1.4 以上運(yùn)行。( WEB-SSO 需要 JDK1.5 以上)
    2. 解開(kāi) desktopsso.zip 文件,里面有兩個(gè)目錄 bin lib
    3. bin 目錄下有一些腳本文件和配置文件,其中 config.properties 包含了三個(gè)需要配置的參數(shù):
      a) SSOServiceURL
      要指向 WebSSO 部署的身份認(rèn)證的 URL
      b) SSOLoginPage
      要指向 WebSSO 部署的身份認(rèn)證的登錄頁(yè)面
      URL
      c) cookiefilepath
      要指向當(dāng)前用戶的 mozilla 所存放 cookie 的文件
    4. bin 目錄下還有一個(gè) login.conf 是用來(lái)配置 JAAS 登錄模塊,本樣例提供了兩個(gè),讀者可以任意選擇其中一個(gè)(也可以都選),再重新運(yùn)行程序,查看登錄認(rèn)證的變化
    5. bin 下的運(yùn)行腳本可能需要作相應(yīng)的修改
      a)
      如果是在 unix 下,各個(gè) jar 文件需要用“ : 來(lái)隔開(kāi),而不是“ ;
      b) java
      運(yùn)行程序需要放置在當(dāng)前運(yùn)行的路徑下,否則需要加上 java 的路徑全名。
    ?
    6.2 桌面樣例的運(yùn)行
    樣例程序包含三個(gè)簡(jiǎn)單的 Java 控制臺(tái)程序,這三個(gè)程序單獨(dú)運(yùn)行都需要登錄。如果運(yùn)行第一個(gè)命叫“ GameSystem 的程序,提示需要輸入用戶名和密碼:
    效驗(yàn)成功以后,便會(huì)顯示當(dāng)前登錄的用戶的基本信息等等。
    ?這時(shí)候再運(yùn)行第二個(gè)桌面Java應(yīng)用(mailSystem)的時(shí)候,就不需要再登錄了,直接就顯示出來(lái)剛才登錄的用戶。
    第三個(gè)應(yīng)用是 logout ,運(yùn)行它之后,用戶便退出系統(tǒng)。再訪問(wèn)的時(shí)候,又需要重新登錄了。請(qǐng)讀者再制裁執(zhí)行完 logout 之后,重新驗(yàn)證一下前兩個(gè)應(yīng)用的 SSO :先運(yùn)行第二個(gè)應(yīng)用,再運(yùn)行第一個(gè),會(huì)看到相同的效果。
    我們的樣例并沒(méi)有在這里停步,事實(shí)上,本樣例不僅能夠和在幾個(gè) Java 應(yīng)用之間 SSO ,還能和瀏覽器進(jìn)行 SSO ,也就是將瀏覽器也當(dāng)成是桌面的一部分。這對(duì)一些行業(yè)有著不小的吸引力。
    這時(shí)候再打開(kāi) Mozilla 瀏覽器,訪問(wèn)以前提到的那兩個(gè) WEB 應(yīng)用,會(huì)發(fā)現(xiàn)只要桌面應(yīng)用如果登錄過(guò), Web 應(yīng)用就不用再登錄了,而且能顯示剛才登錄的用戶的信息。讀者可以在幾個(gè)桌面和 Web 應(yīng)用之間進(jìn)行登錄和 logout 的試驗(yàn),看看它們之間的 SSO
    6.3 桌面樣例的源碼分析
    桌面 SSO 的樣例使用了 JAAS (要了解 JAAS 的詳細(xì)的信息請(qǐng)參考 http://java.sun.com/products/jaas )。 JAAS 是對(duì) PAM Pluggable Authentication Module )的 Java 實(shí)現(xiàn),來(lái)完成 Java 應(yīng)用可插拔的安全認(rèn)證模塊。使用 JAAS 作為 Java 應(yīng)用的安全認(rèn)證模塊有很多好處,最主要的是不需要修改源代碼就可以更換認(rèn)證方式。例如原有的 Java 應(yīng)用如果使用 JAAS 的認(rèn)證,如果需要應(yīng)用 SSO ,只需要修改 JAAS 的配置文件就行了。現(xiàn)在在流行的 J2EE 和其他 Java 的產(chǎn)品中,用戶的身份認(rèn)證都是通過(guò) JAAS 來(lái)完成的。在樣例中,我們就展示了這個(gè)功能。請(qǐng)看配置文件 login.conf
    ??? DesktopSSO {
    ?? desktopsso.share.PasswordLoginModule required;
    ?? desktopsso.share.DesktopSSOLoginModule required;
    };
    當(dāng)我們注解掉第二個(gè)模塊的時(shí)候,只有第一個(gè)模塊起作用。在這個(gè)模塊的作用下,只有 test 用戶(密碼是 12345 )才能登錄。當(dāng)我們注解掉第一個(gè)模塊的時(shí)候,只有第二個(gè)模塊起作用,桌面 SSO 才會(huì)起作用。
    ?
    所有的 Java 桌面樣例程序都是標(biāo)準(zhǔn) JAAS 應(yīng)用,熟悉 JAAS 的程序員會(huì)很快了解。 JAAS 中主要的是登錄模塊( LoginModule )。下面是 SSO 登錄模塊的源碼:
    ? public class DesktopSSOLoginModule implements LoginModule {
    ?? ..........
    ?? private String SSOServiceURL = "";
    ?? private String SSOLoginPage = "";
    ?? private static String cookiefilepath = "";??
    ?? .........
    ?
    config.properties 的文件中,我們配置了它們的值:
    SSOServiceURL=http://wangyu.prc.sun.com:8080/SSOAuth/SSOAuth
    SSOLoginPage=http://wangyu.prc.sun.com:8080/SSOAuth/login.jsp
    cookiefilepath=C:\\Documents and Settings\\yw137672\\Application Data\\Mozilla\\Profiles\\default\\hog6z1ji.slt\\cookies.txt
    SSOServiceURL SSOLoginPage 成員變量指向了在 Web-SSO 中用過(guò)的身份認(rèn)證模塊: SSOAuth ,這就說(shuō)明在桌面系統(tǒng)中我們?cè)噲D和 Web 應(yīng)用共用一個(gè)認(rèn)證服務(wù)。而 cookiefilepath 成員變量則泄露了一個(gè)“天機(jī)”:我們使用了 Mozilla 瀏覽器的 cookie 文件來(lái)保存登錄的憑證。換句話說(shuō),和 Mozilla 共用了一個(gè)保存登錄憑證的機(jī)制。之所以用 Mozilla 是應(yīng)為它的 Cookie 文件格式簡(jiǎn)單,很容易編程訪問(wèn)和修改任意的 Cookie 值。(我試圖解析 Internet Explorer cookie 文件但沒(méi)有成功。)
    下面是登錄模塊DesktopSSOLoginModule的主體: login() 方法。邏輯也是非常簡(jiǎn)單:先用 Cookie 來(lái)登陸,如果成功,則直接就進(jìn)入系統(tǒng),否則需要用戶輸入用戶名和密碼來(lái)登錄系統(tǒng)。
    ??? public boolean login() throws LoginException{
    ??????? try {
    ??????????? if (Cookielogin()) return true;
    ??????? } catch (IOException ex) {
    ??????????? ex.printStackTrace();
    ??????? }
    ????? if (passwordlogin()) return true;
    ????? throw new FailedLoginException();
    ? }
    ?
    下面是Cookielogin() 方法的實(shí)體,它的邏輯是: 先從 Cookie 文件中獲得相應(yīng)的 Cookie 值,通過(guò)身份效驗(yàn)服務(wù)效驗(yàn) Cookie 的有效性。如果 cookie 有效 就算登錄成功;如果不成功或 Cookie 不存在,用 cookie 登錄就算失敗。
    ??? public boolean Cookielogin() throws LoginException,IOException {
    ????? String?cookieValue="";
    ????? int cookieIndex =foundCookie();
    ????? if (cookieIndex<0)
    ??????????? return false;
    ????? else
    ??????????? cookieValue = getCookieValue(cookieIndex);
    ???? username = cookieAuth(cookieValue);
    ???? if (! username.equals("failed")) {
    ???????? loginSuccess =?true;
    ???????? return true;
    ???? }
    ???? return false;
    ? }
    ?
    ?
    用用戶名和密碼登錄的方法要復(fù)雜一些,通過(guò) Callback 的機(jī)制和屏幕輸入輸出進(jìn)行信息交互,完成用戶登錄信息的獲取;獲取信息以后通過(guò) userAuth 方法來(lái)調(diào)用遠(yuǎn)端 SSOAuth 的服務(wù)來(lái)判定當(dāng)前登錄的有效性。
    ?? public boolean passwordlogin() throws LoginException {
    ??? //
    ??? // Since we need input from a user, we need a callback handler
    ??? if (callbackHandler == null) {
    ?????? throw new LoginException("No CallbackHandler defined");
    ??? }
    ??? Callback[] callbacks = new Callback[2];
    ??? callbacks[0] = new NameCallback("Username");
    ??? callbacks[1] = new PasswordCallback("Password", false);
    ??? //
    ??? // Call the callback handler to get the username and password
    ??? try {
    ????? callbackHandler.handle(callbacks);
    ????? username = ((NameCallback)callbacks[0]).getName();
    ????? char[] temp = ((PasswordCallback)callbacks[1]).getPassword();
    ????? password = new char[temp.length];
    ????? System.arraycopy(temp, 0, password, 0, temp.length);
    ????? ((PasswordCallback)callbacks[1]).clearPassword();
    ??? } catch (IOException ioe) {
    ????? throw new LoginException(ioe.toString());
    ??? } catch (UnsupportedCallbackException uce) {
    ????? throw new LoginException(uce.toString());
    ??? }
    ???
    ??? System.out.println();
    ??? String authresult ="";
    ??? try {
    ??????? authresult = userAuth(username, password);
    ??? } catch (IOException ex) {
    ??????? ex.printStackTrace();
    ??? }
    ??? if (! authresult.equals("failed")) {
    ??????? loginSuccess= true;
    ??????? clearPassword();
    ??????? try {
    ??????????? updateCookie(authresult);
    ??????? } catch (IOException ex) {
    ??????????? ex.printStackTrace();
    ??????? }
    ??????? return true;
    ??? }
    ??
    ?
    ??? loginSuccess = false;
    ??? username = null;
    ??? clearPassword();
    ??? System.out.println( "Login: PasswordLoginModule FAIL" );
    ??? throw new FailedLoginException();
    ? }
    ?
    ?
    CookieAuth userAuth 方法都是利用 apahce httpclient 工具包和遠(yuǎn)程的 SSOAuth 進(jìn)行 http 連接,獲取服務(wù)。
    ??????? private String cookieAuth(String cookievalue) throws IOException{
    ??????? String result = "failed";
    ???????
    ??????? HttpClient httpclient = new HttpClient();??????
    ??????? GetMethod httpget = new GetMethod(SSOServiceURL+Action1+cookievalue);
    ???
    ??????? try {
    ??????????? httpclient.executeMethod(httpget);
    ??????????? result = httpget.getResponseBodyAsString();
    ??????? } finally {
    ??????????? httpget.releaseConnection();
    ??????? }
    ??????? return result;
    ??? }
    ?
    private String userAuth(String username, char[] password) throws IOException{
    ??????? String result = "failed";
    ??????? String passwd= new String(password);
    ??????? HttpClient httpclient = new HttpClient();??????
    ??????? GetMethod httpget = new GetMethod(SSOServiceURL+Action2+username+"&password="+passwd);
    ??????? passwd = null;
    ???
    ??????? try {
    ??????????? httpclient.executeMethod(httpget);
    ??????????? result = httpget.getResponseBodyAsString();
    ??????? } finally {
    ??????????? httpget.releaseConnection();
    ??????? }
    ??????? return result;
    ???????
    ??? }
    ?
    還有一個(gè)地方需要補(bǔ)充說(shuō)明的是,在本樣例中,用戶名和密碼的輸入都會(huì)在屏幕上顯示明文。如果希望用掩碼形式來(lái)顯示密碼,以提高安全性,請(qǐng)參考: http://java.sun.com/developer/technicalArticles/Security/pwordmask/
    7 真正安全的全方位 SSO 解決方案: Kerberos
    我們的樣例程序(桌面 SSO WEB-SSO )都有一個(gè)共性:要想將一個(gè)應(yīng)用集成到我們的 SSO 解決方案中,或多或少的需要修改應(yīng)用程序。 Web 應(yīng)用需要配置一個(gè)我們預(yù)制的 filter ;桌面應(yīng)用需要加上我們桌面 SSO JAAS 模塊(至少要修改 JAAS 的配置文件)。可是有很多程序是沒(méi)有源代碼和無(wú)法修改的,例如常用的遠(yuǎn)程通訊程序 telnet ftp 等等一些操作系統(tǒng)自己帶的常用的應(yīng)用程序。這些程序是很難修改加入到我們的 SSO 的解決方案中。
    事實(shí)上有一種全方位的 SSO 解決方案能夠解決這些問(wèn)題,這就是 Kerberos 協(xié)議( RFC 1510 )。 Kerberos 是網(wǎng)絡(luò)安全應(yīng)用標(biāo)準(zhǔn) (http://web.mit.edu/kerberos/) ,由 MIT 學(xué)校發(fā)明,被主流的操作系統(tǒng)所采用。在采用 kerberos 的平臺(tái)中,登錄和認(rèn)證是由操作系統(tǒng)本身來(lái)維護(hù),認(rèn)證的憑證也由操作系統(tǒng)來(lái)保存,這樣整個(gè)桌面都可以處于同一個(gè) SSO 的系統(tǒng)保護(hù)中。操作系統(tǒng)中的各個(gè)應(yīng)用(如 ftp,telnet )只需要通過(guò)配置就能加入到 SSO 中。另外使用 Kerberos 最大的好處在于它的安全性。通過(guò)密鑰算法的保證和密鑰中心的建立,可以做到用戶的密碼根本不需要在網(wǎng)絡(luò)中傳輸,而傳輸?shù)男畔⒁矔?huì)十分的安全。
    目前支持 Kerberos 的操作系統(tǒng)包括 Solaris, windows,Linux 等等主流的平臺(tái)。只不過(guò)要搭建一個(gè) Kerberos 的環(huán)境比較復(fù)雜, KDC (密鑰分發(fā)中心)的建立也需要相當(dāng)?shù)牟襟E。 Kerberos 擁有非常成熟的 API ,包括 Java API 。使用 Java Generic Security Services(GSS) API 并且使用 JAAS 中對(duì) Kerberos 的支持(詳細(xì)信息請(qǐng)參見(jiàn) Sun Java&Kerberos 教程 http://java.sun.com/ j2se/1.5.0/docs/guide/security/jgss/tutorials/index.html ),要將我們這個(gè)樣例改造成對(duì) Kerberos 的支持也是不難的。 值得一提的是在 JDK6.0 http://www.java.net/download/jdk6 )當(dāng)中直接就包含了對(duì) GSS 的支持,不需要單獨(dú)下載 GSS 的包。
    ?
    8 總結(jié)
    本文的主要目的是闡述 SSO 的基本原理,并提供了一種實(shí)現(xiàn)的方式。通過(guò)對(duì)源代碼的分析來(lái)掌握開(kāi)發(fā) SSO 服務(wù)的技術(shù)要點(diǎn)和充分理解 SSO 的應(yīng)用范圍。但是,本文僅僅說(shuō)明了身份認(rèn)證的服務(wù),而另外一個(gè)和身份認(rèn)證密不可分的服務(wù) ---- 權(quán)限效驗(yàn),卻沒(méi)有提到。要開(kāi)發(fā)出真正的 SSO 的產(chǎn)品,在功能上、性能上和安全上都必須有更加完備的考慮。
    作者簡(jiǎn)介
    王昱是 Sun 中國(guó)工程研究院的 Java 工程師,現(xiàn)在的主要負(fù)責(zé)全球合作伙伴的技術(shù)支持。作為一名 Java 資深工程師和架構(gòu)師,王昱在 Java 的很多領(lǐng)域都有多年的造詣,特別是在 Java 虛擬機(jī)、 J2EE 技術(shù) ( 包括 EJB, JSP/Servlet, JMS Web services 等技術(shù) ) 、集群技術(shù)和 Java 應(yīng)用性能調(diào)優(yōu)上有著較為豐富的經(jīng)驗(yàn)。曾經(jīng)多次在重要的 Java 會(huì)議發(fā)表演講,并在國(guó)際著名的 Java 技術(shù)站 點(diǎn)發(fā)表文章。


    posted on 2007-02-01 15:06 java_蟈蟈 閱讀(192) 評(píng)論(0)  編輯  收藏

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 污视频在线观看免费| 33333在线亚洲| 在线观看午夜亚洲一区| 粉色视频成年免费人15次| 亚洲www77777| 亚洲综合激情五月色一区| 亚洲黄页网在线观看| 亚洲AV日韩综合一区尤物| 亚洲色大18成人网站WWW在线播放| 国产精品亚洲自在线播放页码| 亚洲1234区乱码| 亚洲人成人伊人成综合网无码| 亚洲heyzo专区无码综合| 亚洲精品国产av成拍色拍| WWW国产亚洲精品久久麻豆| 99亚洲乱人伦aⅴ精品| 狼色精品人妻在线视频免费| 日本高清不卡中文字幕免费| 国产精品内射视频免费| 永久免费不卡在线观看黄网站| 久久久久久久99精品免费| 日本片免费观看一区二区| 国产美女在线精品免费观看| 国产美女无遮挡免费视频| 日批日出水久久亚洲精品tv| 亚洲日韩v无码中文字幕 | 亚洲男人天堂2018av| 亚洲色精品三区二区一区| 美女黄色毛片免费看| 中文无码日韩欧免费视频| 最近中文字幕大全中文字幕免费 | 国产精品成人免费观看| 免费一级毛片无毒不卡| 免费在线观看视频网站| 成人毛片视频免费网站观看| 亚洲福利精品一区二区三区 | 免费网站看v片在线香蕉| 大胆亚洲人体视频| 亚洲AV无码一区二区乱子伦| 亚洲人成影院午夜网站| 国产一区二区三区亚洲综合|