OMG,到底在尋找什么..................
          (構(gòu)造一個完美的J2EE系統(tǒng)所需要的完整知識體系)
          posts - 198,  comments - 37,  trackbacks - 0
          原貼地址:http://xiecc.itpub.net/post/1476/47438

          發(fā)現(xiàn)我給自己設(shè)了個陷阱,對于
          Acegi Security System 的解析并不是光寫個安全認(rèn)證的流程就能說清楚的。雖然看起來 Acegi 的文檔確實(shí)挺累,但是當(dāng)我動筆寫時卻發(fā)現(xiàn)要寫得比這個文檔更好還是挺難的。畢竟我們不能讓本來很難的事情一下子就變得很簡單了,我也是昨天重要看了一遍了 Acegi Security System 的源代碼,才發(fā)現(xiàn)我自己有明白了好多不明白的東西。但是我仍然希望我寫的東西能夠幫助那些正在學(xué)習(xí)和研究 Acegi Security System 的人們,所以我又開始動筆了,呵呵!

          雖然我不想介紹太多與安全認(rèn)證流程無關(guān)的東西,但是有些東西的講解卻是必不可少的,因?yàn)闆]有這些基本的概念和類,后面的東西就沒法說清了。不過對于具體的類、類圖和它們之間的關(guān)聯(lián),我還是推薦去看 Professional Java Development with the Spring Framework 里關(guān)于 Acegi 的那一章,對于想讀 Acegi 的源代碼和了解 Acegi 內(nèi)部設(shè)計的人來說,這一章真是太有用了。

          本來不想貼這幅類圖的,畢竟有盜版的嫌疑,但是發(fā)現(xiàn)有些東西不貼出來又說不清楚。整個認(rèn)證功能的核心是 Authentication 接口,我們只把 Authentication 想象成一個包含用戶基本信息的類就行了,它里面放了用戶名、密碼、這個用戶的具體權(quán)限有哪些(當(dāng)然具體的東西是由它的子類 UsernamePasswordAuthenticationToken 實(shí)現(xiàn)的,其它類的存放的信息稍有不同,畢竟驗(yàn)證的方式還是多種多樣的,我這里描述的所有東西 Acegi 最常用的實(shí)現(xiàn)方式,而不考慮其它的東西,否則只會分散注意力,看了之后一頭霧水)。 Authentication 里還包含了一個 GrantedAuthority 接口,今天暫且不討論 Authorization 的問題了,畢竟它與驗(yàn)證的流程是不相關(guān)的,而具體的細(xì)節(jié)又極復(fù)雜。

          我們通過 AuthenticationManager 這個接口來驗(yàn)證這個 Authtication 對象的合法性,真正的驗(yàn)證過程看上去很懸,其實(shí)最后的實(shí)現(xiàn)無非是去數(shù)據(jù)庫搜索一下是否存在這個用戶,密碼是否匹配(說的仍然是最常用的實(shí)現(xiàn)方式,呵呵),只是它設(shè)計的時候?qū)ο蟮年P(guān)聯(lián)比較巧妙,類圖看熟了就會覺得沒什么,真正查數(shù)據(jù)庫的那個類是 DaoAuthenticationProvider 。這個接口真正巧妙的地方是它執(zhí)行后返回的結(jié)果是一個 Authentication ,而不是用一個布爾值來表示驗(yàn)證成功或失敗。 Why? 記得當(dāng)年在 JavaEye 上有個討論 Exception 的貼子, robbin 認(rèn)為用戶安全認(rèn)證應(yīng)該用 Checked Exception 來控制流程,更多的人認(rèn)為密碼錯誤是正常的事件流,返回布爾值更為恰當(dāng),這里不討論這兩種觀點(diǎn)的對錯,畢竟每個人站的角度不同,具體的情況也不同。

          但是如果要實(shí)現(xiàn)認(rèn)證的透明性,我們要用到的卻是 unchecked exception ,這個 Exception 叫做 AuthenticationException (如果是 authorization 會拋出 AccessDeniedException, 不過道理類似),這真是奇妙的事,因?yàn)?/span> Exeception 是可以傳遞的,如果在本類里面處理不了這個 Exception ,我們就會將這個 Exception 拋給調(diào)用這個類的類,如此循環(huán),直到有一個類可以處理它為止(對于 Web 來說應(yīng)該是在頁面上提示登錄出錯信息)。沒想到 Exception 的這個種特性用在安全認(rèn)證里如此的合適,權(quán)限不夠?用戶密碼不對?我才不管呢,只要拋出個異常,最后會有人把它接住處理掉的。當(dāng)然這里的另一個條件是 Unchecked ,只有 unchecked 才不會導(dǎo)致接口的污染,才能達(dá)到完全的透明性。

          有了前面的基礎(chǔ)接口,我們要提出下一個問題了,這個 Authentication 對象應(yīng)該存放在哪里?幾乎每個做過 Web 應(yīng)用的人告訴我: HttpSession Acegi 也不例外,雖然還有其它的存放地點(diǎn)(要跟具體的 Web 容器結(jié)合,會導(dǎo)致不兼容性,一般不提倡用)。但是我們馬上會問下一個問題:我們怎么得到 Authentication 對象?當(dāng)然我們可以去 HttpSession 里去取,但是很多時候我們在驗(yàn)證的是與 Web 層無關(guān)的(比如要用戶調(diào)用 Service 層的權(quán)限或 Domain Object 的權(quán)限)。我們必須用與 Web 無關(guān)的 API 來獲取 Authentication 。這讓我們想起了什么?對,是 Webwork Webwork Action 是完全與 Web API 無關(guān)的,它的 Request 里的參數(shù)自動 populate 成了 Action 的屬性,但是我們?nèi)匀豢梢酝ㄟ^ ActionContext 來獲取這些信息。它是怎么做到的?是 Threadlocal ,因?yàn)槊總€ Web Request 都會使 Web 容器生成一個新的線程來處理它的這個特性使我們可以將這些 Web 的數(shù)據(jù)一股腦塞給 Threadlocal 。這個存放 Authentication 的對象叫做 SecurityContext ,而把 SecurityContext 放入 Threadloal 或取出的則是 SecurityContextHolder ,下面就是它的類圖:

          講完這些基礎(chǔ)設(shè)施,我們就可以看具體的認(rèn)證流程啦,真正的認(rèn)證是一串的 Filter (對 Servlet 容器熟悉的人應(yīng)該都不要解釋了)。只不過 Acegi 在這些 Filter 上稍微玩的點(diǎn)花招,因?yàn)橐话愕?/span> Filter 是不能定義在 Spring ApplicationContext 里的,所以這用了一個代理的 Filter 對象 FilterToBeanProxy Filter 操作 Delegate 給定義在 ApplicationContext 里的 Filter 。這個似乎跟主題無關(guān),不過如果以后真有類似的需求的話,這倒是蠻管用的一招。當(dāng)然還有 FilterChainProxy ,它把一串的 Filter 全部定義在一個 bean 里,使配置簡化了好多,呵呵。

          我們來看看 Filter 的一頭一尾。頭是 httpSessionContextIntegrationFilter ,其實(shí)它的功能前面已經(jīng)討論過了,在執(zhí)行前把 HttpSession 里的 Authentication 取出來放到 SessionContextHolder Threadlocal )里,在執(zhí)行完畢后,把 Authentication 塞回到 HttpSession 。真正的實(shí)現(xiàn)代碼有一堆,不過核心的代碼無非就這么幾行:

          Object contextFromSessionObject = httpSession.getAttribute(ACEGI_SECURITY_CONTEXT_KEY);

          SecurityContextHolder.setContext((SecurityContext) contextFromSessionObject);

          chain.doFilter(request, response);

          httpSession.setAttribute(ACEGI_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());

          Filter 的尾是 securityEnforcementFilter ,它的工作就是進(jìn)真正的用戶認(rèn)證的流程控制了,具體的認(rèn)證工作它會 delegate FilterSecurityInterceptor ,但是不管怎么認(rèn)證,結(jié)果無非是認(rèn)證成功或失敗, securityEnforcementFilter 只要管抓住異常再轉(zhuǎn)到特定的頁面就行了。下面就是這個類的信心代碼:

          try {

          filterSecurityInterceptor.invoke(fi);

          }

          } catch (AuthenticationException authentication) {

          sendStartAuthentication(fi, authentication);

          } catch (AccessDeniedException accessDenied) {

          sendAccessDeniedError(fi, accessDenied);

          }

          我們再來看看用戶登錄是怎么干的吧。 Acegi 的用戶登錄很有意思,為了不讓用戶寫任何這方面的代碼,它也直接把這個功能放到 Filter 里了,這個 Filter 叫做 authenticationProcessingFilter 。這個 Filter 的要求是頁面上的 form Action 名字,登錄名、密碼的字段名都是定死的。一個簡單的頁面就這些啦:

          <form action="<c:url value=' j_acegi_security_check '/>" method="POST">

          <input type='text' name='j_username'>

          <input type='password' name='j_password'>

          <input name="submit" type="submit">

          </form>

          記住 action 必須叫 j_acegi_security_check ,用戶名必須叫 j_username ,密碼必須叫 j_password 。呵呵,代碼就不寫了,無非就是判斷只要 Action 名字對,就把用戶名、密碼取出來認(rèn)證一把,最后把認(rèn)證成功的 Authetication 對象填到 SecurityContextHolder 里再導(dǎo)到相應(yīng)頁面,認(rèn)證失敗就導(dǎo)到出錯頁面。

          呵呵,好了,認(rèn)證的核心過程其實(shí)就這些了,雖然還有其它的好多的 Filter 和關(guān)聯(lián),但是當(dāng)我們把核心的內(nèi)容分析清楚之后,其它的都不難了。( Authorization 是例外,有些部分我還沒看明白)。

          posted on 2006-10-13 15:11 OMG 閱讀(446) 評論(0)  編輯  收藏 所屬分類: Spring

          <2006年10月>
          24252627282930
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          IT風(fēng)云人物

          文檔

          朋友

          相冊

          經(jīng)典網(wǎng)站

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 依安县| 连江县| 长治县| 海晏县| 改则县| 西城区| 张家川| 克什克腾旗| 定安县| 麦盖提县| 克拉玛依市| 秦皇岛市| 博乐市| 兴义市| 湖州市| 新宾| 济南市| 滦南县| 敦煌市| 靖西县| 兴化市| 自治县| 敦化市| 资中县| 日喀则市| 祁门县| 沁阳市| 织金县| 莫力| 乌鲁木齐县| 揭阳市| 大理市| 通道| 泽普县| 墨江| 察哈| 吐鲁番市| 岳池县| 郸城县| 哈尔滨市| 连城县|