當前用戶
現在我們能夠開始做一些我們真正關心的事情——執行安全操作。
當保護我們的應用程序時,我們對自己可能提出的最為相關的問題是“當前用戶是誰”或“當前用戶是否被允許做XXX”。
當我們編寫代碼或設計用戶接口時,問這些問題是很常見的:應用程序通常是基於用戶的背景情況建立的,且你想基於每個用戶標准體現(保障)功能。因此,對於我們考慮應用程序安全的最自然的方式是基於當前用戶。
Shiro的API使用它的Subject概念從根本上代表了“當前用戶”的概念。
幾乎在所有的環境中,你可以通過下面的調用獲取當前正在執行的用戶:
Subject currentUser = SecurityUtils.getSubject();
使用 SecurityUtils.getSubject(),我們可以獲得當前正在執行的Subject。Subject是一個安全術語,它基本上的意思是“當前正在執行的用戶的特定的安全視圖”。它並沒有被稱為"User"是因為"User"一詞通常和人類相關聯。
在安全界,術語"Subject"可以表示為人類,而且可是第三方進程,cron job,daemonaccount,或其他類似的東西。它僅僅意味着“該事物目前正與軟件交互”。
對於大多數的意圖和目的,你可以把 Subject 看成是 Shiro 的"User"概念。
getSubject()在一個獨立的應用程序中調用,可以返回一個在應用程序特定位置的基於用戶數據的Subject,並且在服務器環境中(例如,Web 應用程序),它獲取的Subject 是基於關聯了當前線程或傳入請求的用戶數據的。
當前用戶會話
現在你擁有了一個Subject,你能拿它來做什么?
如果你想在應用程序的當前會話中使事物對於用戶可用,你可以獲得他們的會話:
Session session = currentUser.getSession();
session.setAttribute( "someKey", "aValue" );
Session是一個Shiro的特定實例,它提供了大部分你經常與 HttpSessoins使用的東西,除了一些額外的好處以及一
個巨大的區別:它不需要一個 HTTP 環境!
如果在一個Web應用程序內部部署,默認的Session將會是基於 HttpSession 的。但在一個非 Web 環境中,像這
個簡單的教程應用程序,Shiro將會默認自動地使用它的 Enterprise Session Management。這意味着你會使用相同的API在你的應用程序,在任何層,不論部署環境!這開辟了應用程序的新世界,由於任何需要會話的應用程序不必再被強制使用HttpSession或EJB Stateful Session Beans。並且,任何客戶端技術現在能夠共享會話數據。
因此,現在你能獲取一個Subject以及他們的Session。如果他們被允許做某些事,如對角色和權限的檢查,像“檢查”真正有用的地方在哪呢?
權限檢查
嗯,我們只能為一個已知的用戶做這些檢查。我們上面的 Subject實例代表了當前用戶,但誰又是當前用戶?呃,
他們是匿名的——也就是說,直到直到他們至少登錄一次。那么,讓我像下面這樣做:
if ( !currentUser.isAuthenticated() ) {
//collect user principals and credentials in a gui specific manner
//such as username/password html form, X509 certificate, OpenID, etc.
//We'll use the username/password example here since it is the most common.
//(do you know what movie this is from? ;)
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//this is all you have to do to support 'remember me' (no config - built in!):
token.setRememberMe(true);
currentUser.login(token);
}
這就是了!它再簡單不過了。
但如果他們的登錄嘗試失敗了會怎樣?你能夠捕獲各種具體的異常來告訴你到底發生了什么,並允許你去處理並作
出相應反應:
try {
currentUser.login( token );
//if no exception, that's it, we're done!
} catch ( UnknownAccountException uae ) {
//username wasn't in the system, show them an error message?
} catch ( IncorrectCredentialsException ice ) {
//password didn't match, try again?
} catch ( LockedAccountException lae ) {
//account for that username is locked - can't login. Show them a message?
}
... more types exceptions to check if you want ...
} catch ( AuthenticationException ae ) {
//unexpected condition - error?
}
你能夠檢查到許多不同類型的異常,或拋出你自己的自定義條件的異常——Shiro 可能不提供的。請參見AuthenticationException JavaDoc 獲取更多。
Handy Hint
最安全的做法是給普通的登錄失敗消息給用戶,因為你當然不想幫助試圖闖入你系統的攻擊者。
好了,到現在為止,我們已經有了一個登錄用戶。我們還能做些什么?
比方說,他們是是誰:
//print their identifying principal (in this case, a username):
log.info( "User [" + currentUser.getPrincipal() + "] logged in successfully." );
我們也可以測試他們是否有特定的角色:
if ( currentUser.hasRole( "schwartz" ) ) {
log.info("May the Schwartz be with you!" );
} else {
log.info( "Hello, mere mortal." );
}
我們還可以判斷他們是否有權限在一個確定類型的實體上進行操作:
if ( currentUser.isPermitted( "lightsaber:weild" ) ) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
當然,我們可以執行極其強大的實例級權限檢查——判斷用戶是否有能力訪問某一類型的特定實例的能力:
if ( currentUser.isPermitted( "winnebago:drive:eagle5" ) ) {
log.info("You are permitted to 'drive' the 'winnebago' with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
小菜一碟,對吧?
最后,當用戶完成了對應用程序的使用,他們可以注銷:
currentUser.logout(); //removes all identifying information and invalidates their session too.
總結
希望此次推出的教程幫助您了解如何在一個基本的應用程序設置 Shiro 以及 Shiro 的主要設計理念。
關注公眾號Java技術棧回復"面試"獲取我整理的2020最全面試題及答案。
推薦去我的博客閱讀更多:
2.Spring MVC、Spring Boot、Spring Cloud 系列教程
3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程
覺得不錯,別忘了點贊+轉發哦!