[翻譯-Shiro]-Apache Shiro Java認證指南

劉曉日發表於2011-10-08

譯者:劉曉日

認證是身份驗證的過程,也就是試圖驗證一個使用者的有效性。為此,使用者本身就需要提供系統可識別和可信任的身份標識。

這篇指南的目標在於引導如何在java中使用Shiro的認證機制。如果現在還沒做好準備工作,那麼可以先去了解“10分鐘教會你Apache Shiro”來幫助你理解Shiro是如何工作的。

須知術語

Subject:Subject是應用程式中使用者在安全領域特定使用者的縮影。它可以是真實的使用者、第三方程式、連線到應用的server、甚至是corn作業。換句話說,Subject是連線到應用程式的任何東西。

Principals:一個Subject定義的屬性。比如:first name, last name, social security number, username。

Credentials:用來驗證身份的資料。密碼、Biometric data、x509 證照等。

Realms:安全相關的Dao、資料訪問物件、與後臺安全資料來源互動的元件。比如在LDAP中儲存使用者名稱與密碼資訊,那麼就需要一個LDAP realm與LDAP互動。

如何在Java中使用Shiro進行身份驗證

和其他安全框架一樣,Shiro中Java身份認證過程也可以歸結為三個步驟。

步驟

  1. 獲取subject的許可權(principals)與證照(credentials)。
  2. 提交這些許可權與證照到身份認證系統。
  3. 允許訪問,重新認證,或者中斷訪問。

下面是在Shiro中如何實現上述步驟的程式碼。

步驟1:獲取subject的許可權(principals)與證照(credentials)

//最常使用場景例項:
//字元型別使用者名稱與密碼。在系統特定方式下獲取,比如HTTP請求,GUI等等。
UsernamePasswordToken token =
 new UsernamePasswordToken( username, password );
//內建的“Remember Me”是這樣使用的:
token.setRememberMe(true);

在這種情況下,使用的是UsernamePasswordToken類,它是Shiro框架中最常用的認證token。

UsernamePasswordToken用來將從Java應用程式通過某種方式獲取到的使用者名稱和密碼繫結到一起。使用者名稱和密碼可能是通過web表單提交,包含在HTTP請求頭中,抑或通過命令列方式提交,但是在Shiro中通過哪種方式獲取根本不重要,因為UsernamePasswordToken是與協議無關的。

在這個例子中,當使用者返回時,讓應用程式記住使用者狀態。所以當token被建立後,將內建的“Remember Me”啟用,啟用“Remember Me”是通過設定token的setRmemberMe()為true實現的。

步驟2:提交這些許可權與證照到身份認證系統

現在已經在token中記錄下了使用者資訊,並且為再次返回的使用者開啟了“Remember Me”服務。身份認證的下一步是將這個token提交到身份認證系統。這裡的身份認證系統在Shiro中是與安全相關的Dao,也叫做realm。想了解realm更多的資訊,請查閱Shiro Realm指南

在Shiro中盡最大可能讓這個提交的過程快速、容易的完成。實現它僅僅需要一行Java程式碼!

//在Shiro中,通常都希望使用當前正在執行的使用者,也就是subject。
Subject currentUser = SecurityUtils.getSubject();
//認證使用者是通過將包含使用者名稱、密碼資訊的token當做引數傳遞給login方法。
currentUser.login(token);

首先我們需要獲取到當前使用者,也就是subject,subject是應用程式中使用者在安全領域特定使用者的縮影。它可以是真實的使用者、第三方程式、連線到應用的server、甚至是corn作業。在Shiro中,通常都會為當前執行緒繫結一個subject。subject是Shiro的核心概念,其他類似的安全框架也是圍繞subject展開的。在示例中將這個subject例項命名為currentUser。

通過Shiro API的核心類SecurityUtils來獲取當前subject,通過它的getSubject()方法獲取當前正在執行的使用者,而後獲取到代表當前正在與系統互動使用者的subject。示例中的currentUser是沒有身份標識的匿名使用者。

現在已經得到一個使用者例項,然後呼叫login()方法,將剛剛建立好的token提交即可進行身份認證。

步驟3:允許訪問,重新認證,或者中斷訪問

仍然非常非常簡潔,只有一句方法呼叫。如果login()方法正常執行,那麼使用者就成功登陸,並且與特定使用者賬戶或身份標識相關聯。這樣使用者就可以使用應用程式了,可以將自身的標識儲存到session中,由於在示例中設定了“Remember Me”,或許還可以儲存更長時間。

但是如果身份認證失敗會發生什麼事情呢?比如密碼錯誤、訪問系統次數過多,或者使用者賬戶被鎖定等。在Shiro中,如果身份認證失敗,則會丟擲異常,這時Shiro全面的異常層次結構就起到了其應有的作用。

try {
    currentUser.login(token);
} catch ( UnknownAccountException uae ) { ...
} catch ( IncorrectCredentialsException ice ) { ...
} catch ( LockedAccountException lae ) { ...
} catch ( ExcessiveAttemptsException eae ) { ...
} ... catch your own ...
} catch ( AuthenticationException ae ) {
    //unexpected error?
}
//未檢測到異常,則顯示已認證頁面

將login()方法呼叫包含在try/catch塊中,如果想對異常進行處理並作出回應,那麼可以捕獲所有異常序列。除了Shiro本身提供的大量的異常,也可以根據需要建立自定義異常。欲瞭解自定異常更多的資訊,請查閱AuthenticationException

安全提示

安全最佳實踐建議將登陸錯誤資訊反饋給使用者,因為你總不會願意幫助入侵者入侵你的應用程式吧。

“Remember Me”的支援

就像上面示例中展現的那樣,Shiro在常規的登陸過程中支援“remember me”概念。

Shiro中Subject物件提供兩個方法:isRemembered() and isAuthenticated().。

一個“被記住的”subject物件擁有自己的身份標識,屬性(通常也叫做許可權),這些資訊都是在身份認證成功後的session中獲取的。

通過認證的subject在當前session中提供身份標識資訊。

注意

被記住的subject與通過認證的subject是不一樣的。

Remembered vs Authenticated

Shiro中區分被記住的subject和通過認證的subject是非常重要的,因為認證是驗證使用者身份的過程,所以isAuthenticated()方法是一個更嚴格的檢查。當使用者被系統記住,被記住的身份標識僅僅能告訴系統它可能是怎麼的一個使用者,但事實上,是沒辦法保證被記住的Subject就是當前正在使用應用程式的使用者。一旦subject通過身份認證,由於身份標識在當前session中已經得到了驗證,所以它們就不僅僅是“被記住的”。

儘管應用程式中的大部分可以基於記住的principals執行使用者特定的邏輯,比如自定義檢視等。但是在使用者完成身份認證之前都不會執行高度敏感的操作。

比如驗證一個subject是否可以訪問財務資料時,通常都要依靠isAuthenticated()而不是isRemembered()來確認該身份標識是否已通過驗證。

下面這個場景幫助我們理解區分isAuthenticated 和isRemembered的重要性。

就拿使用Amazon.com來說,一天你登陸了,隨後在購物車中新增了幾本書,一天之後,session過期,你自然被系統登出。但是Amazon通過名字記住了你,依然會針對你推薦個性的書籍。這時,對於Amazon來說isRemembered()的返回值就是true。但是如果試圖使用檔案上的信用卡或者更改賬戶資訊時會發生什麼呢?儘管Amozon已經記住了你,isRemembered()也等於true,但是這些並不能確保你就是當前使用者,isAuthenticated()=false。所以在進行敏感操作前Amazon會通過跳轉到登陸頁面強制進行身份標識認證。登陸之後你的身份得到了驗證,這時isAuthenticated()=true。

在web應用中,這個場景經常發生,所以Shiro內建了這個功能來幫助輕鬆的做出區分。

登出

最後,使用者使用應用完畢,則可以登出系統。Shiro中的登出非常快捷和簡單,一句呼叫就搞定。

currentUser.logout(); //刪除所有身份識別資訊,並銷燬session

使用Shiro進行登出時,會刪除subject例項中的身份識別資訊,還會將session銷燬。如果在web環境下使用了Remember Me,logout()方法預設會從瀏覽器中刪除Remember Me的cookie資訊。

原文連結:http://shiro.apache.org/java-authentication-guide.html

相關文章