W eb應(yīng)用的前景是在不斷的演進(jìn)中的,它已經(jīng)從最開(kāi)始作為共享文檔和信息的方式演化為業(yè)務(wù)管理的平臺(tái),而在這種應(yīng)用中,許可和 授權(quán)是一個(gè)關(guān)鍵的特征。Web的應(yīng)用前景還在不斷的演進(jìn)中,而本文把關(guān)注放在面向群體的應(yīng)用中,例如博可和維基。

在這種應(yīng)用中,由于作者希望有交流和反饋,所以授權(quán)不是非常嚴(yán)格的。有時(shí),由于害怕識(shí)別,會(huì)造成失去一些人可能做出的貢獻(xiàn)。但是,缺少授權(quán)就會(huì)帶來(lái)諸如垃圾郵件這類的問(wèn)題。這里有幾條在Web上抽取的信息:

  很明顯,垃圾郵件發(fā)送源必須被識(shí)別出來(lái)。大多數(shù)者類惡意的攻擊發(fā)生數(shù)據(jù)匹配模式被識(shí)出來(lái)之后。一個(gè)可能的方法是人工而不是有計(jì)算機(jī)來(lái)停止這種攻擊,很顯然這是個(gè)挑戰(zhàn)。通過(guò)圖靈測(cè)試,按一個(gè)有名的計(jì)算機(jī)專家――阿蘭。圖靈命名的試驗(yàn),可以確定機(jī)器能夠?qū)崿F(xiàn)類似人類操作的能力。者類測(cè)試中最有名的一個(gè)是CAPTCHA (an acronym for completely automated public Turing test to tell computers and humans apart)。這個(gè)測(cè)試常用來(lái)表明以個(gè)典型的情況:混亂或含義模糊的詞,人很容易識(shí)別,但對(duì)于光學(xué)識(shí)別軟件來(lái)講卻很困難。

  圖1是一個(gè)典型的CAPTCHA.

在J2EE Web 應(yīng)用中使用基于CAPTCHA 的授權(quán)模塊(圖一)


  圖1一個(gè)典型的CAPTCHA.

  現(xiàn)在,大多數(shù)主要的服務(wù)提供商(Yahoo, Hotmail, Google)已經(jīng)在他們的免費(fèi)服務(wù)中使用CAPTCHA,用來(lái)作為區(qū)分垃圾郵件和虛假注冊(cè)的手段。在本文中,我們要描述以下在我們的Web應(yīng)用中加入基于CAPTCHA授權(quán)的方法。

  首先,我們快速瀏覽以下J2EE中Web應(yīng)用的安全模型。

  J2EE安全模型

  在Java開(kāi)發(fā)中,安全性始終是一個(gè)最受關(guān)注的領(lǐng)域。毫無(wú)疑問(wèn),J2EE在構(gòu)建安全的應(yīng)用時(shí),采用了同樣的原理和健壯的框架。在J2EE中,安全性是一個(gè)很大的題目,在這里是不可能敘述細(xì)節(jié)的。在這方面有好多好的資源。我極力推薦團(tuán)隊(duì)和個(gè)人花些時(shí)間來(lái)熟悉這些概念。在這力,我只能極概括的敘述一些最關(guān)鍵的概念。

  關(guān)鍵概念

  在J2EE應(yīng)用中,安全性必須采用聲明或編程的話方法。就象名字中暗示的,當(dāng)采用聲明方法時(shí),開(kāi)發(fā)者在應(yīng)用軟件代碼的外部定義用于應(yīng)用的安全性約束。這些聲明用部署描述符的形式來(lái)建造(web.xml, ejb-jar.xml),并由容器的運(yùn)行環(huán)境來(lái)保證它的強(qiáng)制執(zhí)行。聲明的方式容許開(kāi)發(fā)者:

  ·能夠?qū)崿F(xiàn)基于身份的對(duì)資源存取的約束(例如:/admin/* 只能容許有管理員身份的人來(lái)操作)
  ·能夠?qū)崿F(xiàn)對(duì)某些URL的存取只能用某種協(xié)議的約束(例如:“/customer/*”只能通過(guò)HTTPS來(lái)訪問(wèn))
  ·能夠?qū)崿F(xiàn)基于身份的對(duì)某些服務(wù)存取的約束(例如:可以限定SiteShutdownServlet只能由具有“god”身份的人來(lái)操作)
  ·能夠?qū)崿F(xiàn)當(dāng)用戶要存取某些受限資源但用戶還沒(méi)有登錄到系統(tǒng)的時(shí)候,自動(dòng)重定向到登錄頁(yè)面的功能.而編程的方法能提供查詢和調(diào)用安全設(shè)施的機(jī)制,而開(kāi)發(fā)者必須實(shí)現(xiàn)這些機(jī)制。這個(gè)方法的特點(diǎn)是:
  ·檢索出與當(dāng)前用戶相關(guān)聯(lián)的部分:HttpServletRequest.getUserPrincipal or EJBContext.getCallerPrincipal
  ·查詢用戶是否具有某種特定的身份:HttpServletRequest.isUserInRole(String role) or EJBContext.isCallerInRole(String role)

  這兩種方法都有它的局限性,并且是能互相補(bǔ)充的。

  Web應(yīng)用的聲明安全的方法

  Web應(yīng)用的聲明安全的方法本質(zhì)上是一種被動(dòng)的方法。這意味者只有在剛開(kāi)始訪問(wèn)受保護(hù)資源的用戶,如果他們沒(méi)有被受權(quán),才會(huì)被重定向到登錄頁(yè)面。如果這個(gè)用戶已經(jīng)被授權(quán)并有適當(dāng)?shù)臋?quán)限,他們就能訪問(wèn)這些資源。

  這類方法中有一個(gè)最常用的方法是基于規(guī)則的受權(quán)。應(yīng)用部署描述符web.xml分兩個(gè)部分描述了在這個(gè)方法中需要的所有元素。

  第一部分是適用于整個(gè)應(yīng)用的。它要鑒別出:

  ·在登錄中需要使用的方法。J2EE支持BASIC,DIGEST,F(xiàn)ORM,或CERT等幾種授權(quán)機(jī)制。      
  ·用于基于規(guī)則受權(quán)的登錄和錯(cuò)誤的頁(yè)面
  ·能在應(yīng)用中使用的所有身份的超集


  圖2表明了第一部分的關(guān)鍵元素和它們之間的關(guān)系

在J2EE Web 應(yīng)用中使用基于CAPTCHA 的授權(quán)模塊(圖二)


  圖2 第一部分的關(guān)鍵元素和它們之間的關(guān)系
  
  第二部分說(shuō)明了資源方面的約束。部署描述符可以包含零個(gè)或多個(gè)類似于下面的聲明:

  ·需要保護(hù)的站點(diǎn)。這可以在web-resource-collection內(nèi)使用url-pattern來(lái)配置。
  ·能夠存取資源的身份的集合(auth-constraint)。它通常是第一部分定義的身份集合的一個(gè)子集。
  ·與某個(gè)資源相關(guān)的傳輸?shù)谋WC(user-data-constraint)。

  圖3表明了第二部分的關(guān)鍵元素和它們之間的關(guān)系

在J2EE Web 應(yīng)用中使用基于CAPTCHA 的授權(quán)模塊(圖三)


  圖3 第二部分的關(guān)鍵元素和它們之間的關(guān)系

  現(xiàn)在,我們看一個(gè)簡(jiǎn)單的例子web.xml:


<web-app>

  <!-- ... -->

  <!--
    Define the security constraint.  This will limit the /admin/* portion of
    the application to only be accessible to users within the "admin" role.
    When an unauthenticated user attempts to access this section of the site,
    they will be automatically presented with the login page.
  -->
  <security-constraint>

    <!-- Define the context-relative URL(s) to be protected -->
    <web-resource-collection>
      <web-resource-name>Protected Area</web-resource-name>
      <url-pattern>/admin/*</url-pattern>
      <http-method>GET</http-method>
      <http-method>POST</http-method>
    </web-resource-collection>

    <!-- Define the roles that are allowed to access this URL with the given methods -->
    <auth-constraint>
      <role-name>admin</role-name>
    </auth-constraint>

    <!-- Transport guarantee could be used to guarantee an HTTPS protocol -->
    <user-data-constraint>
      <transport-guarantee>NONE</transport-guarantee>
    </user-data-constraint>

  </security-constraint>

  <!--
    Define the method for authenticating a user when requesting a restricted
    page.  Methods include BASIC (the simple pop-up dialog), FORM and
    CERTIFICATE.
  -->
  <login-config>
    <!-- We will use form based authentication -->
    <auth-method>FORM</auth-method>
    <realm-name>Default Realm</realm-name>

    <!-- where should the user be forwarded to enter his credentials -->
    <form-login-config>
      <form-login-page>/login/login.jsp</form-login-page>
      <!--
        On error the user will be shows this page It can also server side
        forward back to the login page, which is popular behavior for most
        sites.
      -->
      <form-error-page>/login/error.jsp</form-error-page>
    </form-login-config>
  </login-config>

  <!--
    Finally a list of all security roles in the application must be given.
  -->
  <security-role>
    <description>Capable of administrating the site</description>
    <role-name>admin</role-name>
  </security-role>
</web-app>

  這個(gè)簡(jiǎn)單的部署描述符包含以下幾部分的安全配置:

  ·約束對(duì)有以/admin/*模式開(kāi)頭的URLs的存取(URLl模式)
  ·在/admin下的資源只能使用HTTP GET或POST來(lái)存取(HTTP方法)
  ·資源能在標(biāo)準(zhǔn)的HTTP連接方式下提供服務(wù)(傳輸保證)
  ·只有具有管理員身份的用戶才能存取這些資源(身份命名)
  ·對(duì)遠(yuǎn)程用戶使用基于規(guī)則的授權(quán)(授權(quán)方法)
  ·給用戶顯示一個(gè)登錄的頁(yè)面――/login/login.jsp ,以便用戶輸入信息來(lái)確認(rèn)身份(形成登錄頁(yè)面)
  ·如果在授權(quán)過(guò)程中發(fā)生錯(cuò)誤,給用戶顯示以個(gè)頁(yè)面來(lái)提示出錯(cuò)――/login/error.jsp(形成出錯(cuò)頁(yè)面)

  擴(kuò)展一個(gè)容器的安全設(shè)施
  
  JAAS(Java Authentication and Authorization Services)實(shí)現(xiàn)了一個(gè)JAVA應(yīng)用的可插入性授權(quán)模塊(PAM)。它容許平行開(kāi)發(fā)安全部分和應(yīng)用部分。開(kāi)發(fā)者可以從這些選項(xiàng)中選擇并在應(yīng)用中配置。由于容許與應(yīng)用平行開(kāi)發(fā),所以JAAS有一些優(yōu)點(diǎn),還可以促進(jìn)它在不同的應(yīng)用中重用。

  JAAS在應(yīng)用的服務(wù)端也同樣有價(jià)值。然而,JAAS在J2EE中并沒(méi)有取得同樣的成功。直到最近,才開(kāi)放了一些可定制的API用于擴(kuò)展安全設(shè)施。但情況在改變。應(yīng)用的服務(wù)端現(xiàn)在提供了適配器,可以容許把JAAS整合近已有的安全設(shè)施。這種整合仍然是與具體的應(yīng)用相關(guān)的,并且非常復(fù)雜。

  Tomcat提供了一種使用JAAS的相當(dāng)簡(jiǎn)單和直接整合的方法。用戶登錄模塊是用配置文件來(lái)配置的(Tomcat realm configuration and the standard JAAS configuration)。當(dāng)服務(wù)端需要調(diào)用登錄模塊時(shí),它把所有的請(qǐng)求都路由到org.apache.catalina.realm.JAASRealm適配器。把JAAS整合進(jìn)Tomact的細(xì)節(jié)可參考相應(yīng)的資料Resources。

  在這篇文章,我們實(shí)現(xiàn)了一個(gè)JAAS模塊,并把它整合近Tomcat服務(wù)端,以提供J2EE的安全解決方案。

  解決方法

  在敘述實(shí)現(xiàn)方面之前,先描述一下解決方案的目標(biāo)和所用的方法。

  目標(biāo):

  ·為Web應(yīng)用提供一種較弱的授權(quán)機(jī)制。這里,較弱的授權(quán)機(jī)制的含義是安全模塊或應(yīng)用不區(qū)分用戶是否是在遠(yuǎn)程訪問(wèn)。
  ·不要求每個(gè)用戶在系統(tǒng)中有惟一的一個(gè)標(biāo)識(shí)(登錄名)。這可以對(duì)遠(yuǎn)程用戶做一定的隱藏。
  ·這種授權(quán)機(jī)制能區(qū)分計(jì)算機(jī)和用戶,這樣可以防止垃圾郵件的源自動(dòng)登錄并濫用資源。我們用CAPTCHAs來(lái)做測(cè)試。
  ·這個(gè)授權(quán)機(jī)制應(yīng)該基于J2EE的安全模型。我們要避免與這個(gè)模型不一致的方法。

  根據(jù)以上的目標(biāo),很顯然,我們要保證每次會(huì)話中都是由實(shí)際的用戶參與的。應(yīng)用的服務(wù)端管理會(huì)話來(lái)保持用戶的狀態(tài)。當(dāng)一個(gè)未授權(quán)的用戶訪問(wèn)受保護(hù)的資源的時(shí)候,J2EE的安全模塊要把用戶轉(zhuǎn)到登錄頁(yè)面。這個(gè)登錄頁(yè)面產(chǎn)生以個(gè)惟一的CAPTCHA并把它與用戶的會(huì)話聯(lián)系起來(lái)。這個(gè)登錄頁(yè)面就以一幅圖像的形式顯示這個(gè)CAPTCHA,并要求用戶識(shí)別這幅圖像。這個(gè)登錄頁(yè)面還有一個(gè)包含當(dāng)前會(huì)話ID的隱藏起來(lái)的輸入域。

  用戶把自己識(shí)別的結(jié)果添入輸入域并提交。在得到反饋后,負(fù)責(zé)登錄的模塊取出對(duì)話的ID和用戶的反饋。然后,把反饋與這個(gè)會(huì)話相關(guān)聯(lián)的CAPTCHA相比較。如果匹配,那么這個(gè)對(duì)這個(gè)用戶的鑒別就通過(guò)了,并把這個(gè)用戶的身份定為anonymous。

  在授權(quán)后,遠(yuǎn)端的用戶就可以以anonymous用戶的身份來(lái)存取所有受保護(hù)的資源。

  圖4說(shuō)明了我們的方法

在J2EE&amp;nbsp;Web&amp;nbsp;應(yīng)用中使用基于CAPTCHA&amp;nbsp;的授權(quán)模塊(圖四)


  圖4 我們的方法

  實(shí)現(xiàn)

  實(shí)現(xiàn)一個(gè)新授權(quán)機(jī)制的過(guò)程是相當(dāng)明了的。整個(gè)過(guò)程可分為四個(gè)過(guò)程:

  ·保護(hù)Web資源
  ·為每個(gè)會(huì)話產(chǎn)生以個(gè)惟一的標(biāo)識(shí)
  ·與已有的安全容器整合
  ·測(cè)試

  下面詳細(xì)敘述以下這些過(guò)程。

  保護(hù)Web資源

  Web資源保護(hù)用J2EE聲明安全機(jī)制。下面的是web.xml文件的一個(gè)片斷,顯示了如何實(shí)現(xiàn)要求的配置。
<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app>

  <!-- constrain a  section of the site -->
  <security-constraint>
      <display-name>Anonymous Security Constraint</display-name>
      <web-resource-collection>
         <web-resource-name>Protected Area</web-resource-name>
         <url-pattern>/security/protected/*</url-pattern>
         <http-method>GET</http-method>
         <http-method>POST</http-method>
      </web-resource-collection>
      <auth-constraint>
         <role-name>anonymous</role-name>
      </auth-constraint>
  </security-constraint>

  <!-- Default login configuration uses form-based authentication -->

  <login-config>
      <auth-method>FORM</auth-method>
      <realm-name>Anonymous Form-Based Authentication Area</realm-name>
      <form-login-config>
        <form-login-page>/security/protected/login.jsp</form-login-page>
        <form-error-page>/security/protected/error.jsp</form-error-page>
      </form-login-config>
  </login-config>
        
  <!-- Security roles referenced by this web application -->
  <security-role>
     <role-name>anonymous</role-name>
  </security-role>

  <!-- The Usual Welcome File List -->
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

</web-app>

  這個(gè)片斷包含了這樣一些內(nèi)容:

  ·所有保存在/security/protected目錄下的資源(JSP網(wǎng)頁(yè),SERVLETS)都要受容器的保護(hù)
  ·Anonymous是惟一的可訪問(wèn)這些資源的用戶
  ·這個(gè)容器只接受HTTP 方式的GET 和POST請(qǐng)求
  ·受保護(hù)的資源只能通過(guò)常規(guī)的HTTP連接方式提供服務(wù)
  ·使用基于規(guī)則的授權(quán);security/protected/login.jsp and /security/protected/error.jsp 分別作為登錄和出錯(cuò)的頁(yè)面

  在寫好安全部分后,就需要寫登錄和出錯(cuò)的頁(yè)面。由于把每個(gè)會(huì)話與一個(gè)CAPTCHA或標(biāo)識(shí)相關(guān)聯(lián),所有l(wèi)ogin.jsp 包含到TokenServlet的請(qǐng)求。

  login.jsp的一部分:
.....
.....
<%-- Generates and associates a CAPTCHA --%>
<img src="/servlet/AuthToken" alt="Your authentication token"/>

<%-- The form login page --%>
<form method="POST" action='<%= response.encodeURL("j_security_check") %>' >
  <table border="0" cellspacing="5">
    <input type="hidden" name="j_username" value="<%= session.getId() %>">
    <tr>
      <th align="right">Challenge:</th>
      <td align="left"><input type="password" name="j_password"></td>
    </tr>
    <tr>
      <td align="right"><input type="submit" value="Log In"></td>
      <td align="left"><input type="reset"></td>
    </tr>
  </table>
</form>
.....
.....

  登錄頁(yè)面使用基于規(guī)則的安全方法,并把數(shù)據(jù)提交給j_security_check。j_username輸入域被隱藏起來(lái),默認(rèn)值是用戶會(huì)話的ID。另一個(gè)域,j_password是用戶輸入標(biāo)識(shí)的地方。

  出錯(cuò)頁(yè)面是相當(dāng)簡(jiǎn)單的。它只是提示災(zāi)害授權(quán)過(guò)程中發(fā)生樂(lè)和錯(cuò)誤,還提供了以個(gè)到登錄頁(yè)面的連接。

  產(chǎn)生新會(huì)話的惟一的標(biāo)識(shí)

  下面,需要提供與新會(huì)話相關(guān)聯(lián)的標(biāo)識(shí)符的方法。

  為支持產(chǎn)生多個(gè)標(biāo)識(shí),我們采用抽象工廠模式。jw.token.factory.TokenFactory是抽象工廠模式,根據(jù)給定的參數(shù),初始化一個(gè)具體的標(biāo)識(shí)符產(chǎn)生工廠,并將其返回。如果沒(méi)有要求一個(gè)特定的標(biāo)識(shí)符產(chǎn)生工廠,就會(huì)返回一個(gè)默認(rèn)的實(shí)現(xiàn)。Resources里有些分布式的可用的標(biāo)識(shí)符產(chǎn)生工廠可以下載。第一個(gè)是我的業(yè)余練習(xí)jw.token.factory.SimpleTokenFactory。第二個(gè)是jw.token.Token,式由Jcaptcha項(xiàng)目提供的一個(gè)更成熟的實(shí)現(xiàn)。可以有g(shù)etToken()來(lái)調(diào)用。標(biāo)識(shí)符對(duì)象是一個(gè)用于CAPTCHAs的容器,可以通過(guò)在調(diào)用getTokenImage()時(shí)被當(dāng)作一幅圖像來(lái)渲染。

  圖5描述了標(biāo)識(shí)符工廠對(duì)象模型

在J2EE&amp;nbsp;Web&amp;nbsp;應(yīng)用中使用基于CAPTCHA&amp;nbsp;的授權(quán)模塊(圖五)


  圖5 標(biāo)識(shí)符工廠和標(biāo)識(shí)符類的對(duì)象模型

  一旦遠(yuǎn)程客戶被定向到登錄頁(yè),它要調(diào)用jw.jaas.servlet.TokenGeneratorServlet來(lái)取回以個(gè)標(biāo)識(shí)符。TokenServlet的行為對(duì)于所有的標(biāo)識(shí)符產(chǎn)生請(qǐng)求類似于看門人,它在web.xml中被配置。它接受一個(gè)初始化參數(shù)――tokenFactory,這個(gè)參數(shù)指明了要使用的標(biāo)識(shí)符產(chǎn)生工廠。Servlet把請(qǐng)求委托給這個(gè)工廠,得到一個(gè)標(biāo)識(shí)符,稍后把標(biāo)識(shí)符作為一幅圖像傳給遠(yuǎn)方的Web客戶端。

  圖6說(shuō)明了標(biāo)識(shí)符產(chǎn)生的順序

在J2EE&amp;nbsp;Web&amp;nbsp;應(yīng)用中使用基于CAPTCHA&amp;nbsp;的授權(quán)模塊(圖六)


  圖6 標(biāo)識(shí)符產(chǎn)生順序

  為存儲(chǔ)與會(huì)話相關(guān)聯(lián)的標(biāo)識(shí)符,我們創(chuàng)建以個(gè)緩沖。jw.token.AuthenticationTokenCache,它作為一個(gè)標(biāo)識(shí)符的倉(cāng)庫(kù),還提供以個(gè)接口,以實(shí)現(xiàn)與某個(gè)會(huì)話相關(guān)的標(biāo)識(shí)符的查詢、刪除,和新標(biāo)識(shí)符的加入。緩沖是惟一的,并采用同步映射。

  我們也需要周期的清理AuthenticationTokenCache。在web.xml中注冊(cè)jw.token.TokenInvalidationListener作為一個(gè)會(huì)話的監(jiān)聽(tīng)器。因此,不論何時(shí),當(dāng)一個(gè)會(huì)話被銷毀時(shí),sessionDestroyed()總會(huì)被調(diào)用,然后標(biāo)識(shí)符就從緩沖中被清除。

  圖7顯示了服務(wù)模塊、緩沖和監(jiān)聽(tīng)器對(duì)象間的關(guān)系

在J2EE&amp;nbsp;Web&amp;nbsp;應(yīng)用中使用基于CAPTCHA&amp;nbsp;的授權(quán)模塊(圖七)


  圖7服務(wù)模塊、緩沖和監(jiān)聽(tīng)器對(duì)象間的關(guān)系

  Web.xml顯示了servlet的配置
<web-app>
  .....
  .....

  <listener>
     <listener-class>jw.jaas.servlet.TokenInvalidationListener</listener-class>
  </listener>

  <!-- Standard Action Servlet Configuration (with debugging) -->
  <servlet>
    <servlet-name>tokengen</servlet-name>
    <servlet-class>jw.jaas.servlet.TokenGeneratorServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
    <init-param>
      <param-name>tokenFactory</param-name>
      <param-value>jw.token.factory.SimpleTokenFactory</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>tokengen</servlet-name>
    <url-pattern>/servlet/AuthToken</url-pattern>
  </servlet-mapping>

  .....
  .....
</web-app>

  在部署描述符中,路徑/servlet/AuthToken被映射到TokenGeneratorServlet。Servlet被配置使用SimpleTokenFactory。

  實(shí)現(xiàn)JAAS模塊

  在JAAS Authentication Guide是說(shuō)明實(shí)現(xiàn)JAAS登錄模塊的很好的參考資料。登錄模塊包含涉及生命周期的方法,如initialize(),login(),logout(),abort()和commit(),它有登錄上下文調(diào)用。大多數(shù)容器在它們的安全設(shè)施中提供插入JAAS登錄模塊的適配器。在Tomcat 中是JAASRealm。它截取登錄信息并產(chǎn)生一個(gè)回調(diào)句柄――JAASCallbackHandler,有能力處理兩種回調(diào)――NameCallback和PasswordCallback。這些回調(diào)分別包含在j_username 和 j_password輸入域中的值。然后,回調(diào)句柄被傳遞給登錄模塊來(lái)執(zhí)行授權(quán)操作。

  圖8顯示了登錄模塊類的對(duì)象模型

在J2EE&amp;nbsp;Web&amp;nbsp;應(yīng)用中使用基于CAPTCHA&amp;nbsp;的授權(quán)模塊(圖八)


  圖8登錄模塊類的對(duì)象模型

  登錄模塊中的大多數(shù)的方法(commit(), abort(), initialize(), logout())是自解釋的。讓我們仔細(xì)看一下login()方法。

  Login()方法從回調(diào)句柄中取得會(huì)話ID和用戶的回答。接下來(lái),它會(huì)查詢AuthenticationTokenCache,以取得與這個(gè)會(huì)話相關(guān)聯(lián)的標(biāo)識(shí)符。用戶的輸入會(huì)與標(biāo)識(shí)符對(duì)象的內(nèi)部狀態(tài)相比較。如果成功,用戶的會(huì)話會(huì)與一個(gè)很重要的調(diào)用AnonymousPrincipal相關(guān)聯(lián)。由于應(yīng)用只對(duì)較弱的授權(quán)機(jī)制感興趣,所以你可以把這看做類似于把每個(gè)在你網(wǎng)站上的用戶命名為anonymous.

  登錄上下文負(fù)責(zé)處理這個(gè)過(guò)程,并確保用戶已經(jīng)登錄。用戶現(xiàn)在就已經(jīng)登錄到系統(tǒng),并有anonymous的身份。

  圖9顯示了login()的順序圖

在J2EE&amp;nbsp;Web&amp;nbsp;應(yīng)用中使用基于CAPTCHA&amp;nbsp;的授權(quán)模塊(圖九)

點(diǎn)擊查看大圖

 

 

 


  圖9 login()的順序圖

  與容器的安全設(shè)施整合

  現(xiàn)在考慮把各個(gè)部分與容器的安全設(shè)施相整合。這是一個(gè)與服務(wù)相關(guān)的步驟。Tomcat為每個(gè)Web 應(yīng)用管理一個(gè)配置文件。這個(gè)文件命名為.xml,放在/conf/Catalina/localhost文件夾下。把這個(gè)文件替換為下面的內(nèi)容:

%lt;?xml version='1.0' encoding='utf-8'?>
%lt;Context debug="9" docBase="d:/work/captcha-login/web" path="/clogin">
  %lt;Realm className="org.apache.catalina.realm.JAASRealm" appName="clogin" debug="99" roleClassNames="jw.jaas.AnonymousPrincipal" userClassNames="jw.jaas.AnonymousPrincipal"/>
%lt;/Context>

  此外,我們要求JAAS配置直接與應(yīng)用對(duì)應(yīng)的登錄模塊。
JAAS配置文件:

clogin {
   jw.jaas.AnonymousLoginModule required debug=true;
};


  為簡(jiǎn)便起見(jiàn),編譯腳本要自動(dòng)產(chǎn)生所有的配置條目。

  測(cè)試

  為測(cè)試安全模塊,我建了一個(gè)Web應(yīng)用,叫Anonymous Bulletin Board(ABB),這是一個(gè)容許用戶討論日常問(wèn)題的應(yīng)用,它的主頁(yè)要列出迄今所有發(fā)表的貼子。用戶也可以在上面發(fā)貼子。容許發(fā)貼子的頁(yè)是用生命安全的方式來(lái)保護(hù)的。只有通過(guò)授權(quán)的用戶才能發(fā)貼子。
圖10顯示了這種應(yīng)用的一種屏幕顯示。

在J2EE&amp;nbsp;Web&amp;nbsp;應(yīng)用中使用基于CAPTCHA&amp;nbsp;的授權(quán)模塊(圖十)

點(diǎn)擊查看大圖

 

 

 


  圖10這種應(yīng)用的一種屏幕顯示

  必要的準(zhǔn)備

  為了構(gòu)造和測(cè)試這個(gè)應(yīng)用,我們需要Web container version 5.0或更高的版本,還有Ant build environment version 1.5或更高的版本。這些可以從Apache下載和安裝。一旦有了這些準(zhǔn)備,我們就可以安下面的步驟來(lái)構(gòu)造和部署應(yīng)用了。

  安裝和測(cè)試

  ·下載并解壓縮jw-0307-captcha.zip到一個(gè)目錄(如:d:\captcha)。解壓縮出來(lái)的文件會(huì)包括源代碼、庫(kù)文件、Web應(yīng)用和編譯腳本。
  ·轉(zhuǎn)到文件被解壓縮到的目錄,編輯文件setAntEnv.cmd。象文件名暗示的那樣,這個(gè)文件包含開(kāi)發(fā)環(huán)境啟動(dòng)時(shí)所需要的變量。編輯變量ANT_HOME和JAVA_HOME,使其指向正確的位置。所有其它的部分都是從這兩個(gè)變量導(dǎo)出的,因此不需要改動(dòng)。
  ·還是在這個(gè)目錄下,編輯文件ant.developer.properties。這個(gè)文件包含影響應(yīng)用編譯采用的方法的部分。文件中的大部分都有一個(gè)有意義的默認(rèn)值,你只需要設(shè)置build.home和server.home。
  ·現(xiàn)在,打開(kāi)命令行窗口,并轉(zhuǎn)到這個(gè)目錄(d:\captcha),并執(zhí)行setAntEnv.cmd批處理腳本。
  ·輸入ant compile jar deploy來(lái)編譯并部署這個(gè)應(yīng)用。Ant腳本首先創(chuàng)建需要的目錄,編譯并安全模塊代碼,并打包為JAR文件。最后部署它。
  ·通過(guò)輸入ant start命令來(lái)啟動(dòng)應(yīng)用的服務(wù)端。Tomcat應(yīng)該在以個(gè)獨(dú)立的窗口啟動(dòng)并運(yùn)行。
  ·打開(kāi)瀏覽器,讓它指向http://localhost:8080/clogin (clogin是在ant.developer.properties 中配置的application.name屬性)。
  ·在測(cè)試完畢后,你可以通過(guò)關(guān)閉Tomact窗口的方式來(lái)停止服務(wù)。

  無(wú)論什么時(shí)候改變了ant.developer.properties中的屬性,都要重新編譯,以保證所有的屬性的依賴都是正確的。例如,如果你使用了out-of-box配置,標(biāo)示符服務(wù)器將使用簡(jiǎn)單的標(biāo)示符工廠。你也可以把文件ant.developer.properties中的captcha.token.factory的值改為jw.token.factory.JCaptchaTokenFactory來(lái)做更復(fù)雜的測(cè)試。運(yùn)行ant clean-all deploy來(lái)傳播變化。啟動(dòng)服務(wù)端并測(cè)試應(yīng)用。

  調(diào)試

  根據(jù)你的環(huán)境的不同,應(yīng)用在啟動(dòng)的時(shí)候可能會(huì)失敗。編譯腳本已經(jīng)部署了一個(gè)用于Tomcat配置的的名為log4j的文件。你可以打開(kāi)/logs/jaasModule.log或/logs/tomcat.log,看一下追蹤和調(diào)試級(jí)的信息。jaasModule.log包含了登錄模塊和Web應(yīng)用的調(diào)試信息。tomcat.log捕捉了從容器和部署的代碼產(chǎn)生的登錄消息。打開(kāi)這些文件來(lái)尋找線索。

  結(jié)論

  在這篇文章中,我們?yōu)g覽了J2EE Web應(yīng)用的聲明安全模型,還有擴(kuò)展它來(lái)使用JAAS登錄模塊的方法。我們擴(kuò)展了Tomcat的J2EE安全設(shè)施以支持一種用戶定義的授權(quán)機(jī)制。這個(gè)授權(quán)機(jī)制是作為一個(gè)JAAS登錄模塊實(shí)現(xiàn)的,并在較弱的用戶授權(quán)中使用了CAPTCHSs。對(duì)于某種應(yīng)用領(lǐng)域,它能確定遠(yuǎn)端用戶是一個(gè)人。使用容器提供的安全機(jī)制,我們可以很容易的確保應(yīng)用的安全而不用更改任何代碼。

  應(yīng)該注意到,CAPTCHAa既可以產(chǎn)生對(duì)用戶有用的結(jié)果,也可以產(chǎn)生無(wú)用的結(jié)果。當(dāng)開(kāi)發(fā)者在選擇授權(quán)機(jī)制的時(shí)候應(yīng)該注意到這個(gè)問(wèn)題。

  關(guān)于作者

  Anand Raman作為技術(shù)方面的合作者為Sapient在德里以外的地方工作。在過(guò)去的五年中一直從事JAVA和J2EE相關(guān)的技術(shù)工作。追蹤J2EE的復(fù)雜性是他最感興趣的。