Shiro的subject實質上是當前執行用戶的特定視圖。
通過org.apache.shiro.SecurityUtils可以查詢當前執行用戶:
Subject currentUser = SecurityUtils.getSubject();
獲取當前執行用戶的session:
(在非web、非EJB的情況下,Shiro自動使用自帶session;如果是web或者EJB應用,則Shiro自動使用HttpSession,不需要人為改變。)
Session session = currentUser.getSession();
session.setAttribute( "someKey", "aValue" );
案例一:
驗證用戶是否為認證用戶:
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); }
驗證失敗,提示信息:
ry {
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? }
案例二(展示當前用戶信息):
//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."); }
案例五(退出登錄):
currentUser.logout(); //removes all identifying information and invalidates their session too.
Shiro支持創建subject的實例,但不推薦。因為我們平常可以直接通過getSubject來獲取當前執行用戶。個別情況需要創建subject:
1.當前沒有用戶可以與系統進行交互,但是為保持系統的運行,需要假設一個用戶,此時可以創建一個subject,比如admin用戶。
2.集成測試時,需要創建一個臨時的subject用以進入下一步的測試。
3.應用的后台進程運行的時候,需要一個subject。
(如果已經擁有了一個subject,但是需要和其他線程共享的話,需要調用Subject.associateWith*方法。)
subject的創建
案例六(Subject.Builder,創建subject,而無需知道其中細節,會訪問到SecurityManager的SecurityUtils.getSecurityManager()方法。):
Subject subject = new Subject.Builder().buildSubject()
案例七(自建securityManager):
SecurityManager securityManager = //acquired from somewhere Subject subject = new Subject.Builder(securityManager).buildSubject();
案例八(利用session創建新的subject):
Serializable sessionId = //acquired from somewhere
Subject subject = new Subject.Builder().sessionId(sessionId).buildSubject();
案例九(創建subject,並將其屬性映射到驗證屬性中):
Object userIdentity = //a long ID or String username, or whatever the "myRealm" requires String realmName = "myRealm"; PrincipalCollection principals = new SimplePrincipalCollection(userIdentity, realmName); Subject subject = new Subject.Builder().principals(principals).buildSubject();
將自建的subject與線程進行綁定:
1.系統的自動綁定,Subject.execute*方法的調用。
2.手動的綁定。
3.利用已綁定的線程來綁定到新的線程,Subject.associateWith*方法的調用。
(subject與線程綁定,則可以在線程執行過程中取到信息;subject與線程取消綁定,則線程可以被回收。)
案例十(調用execute方法,參數為runable實例,實現subject的自動綁定與拆除):
Subject subject = //build or acquire subject
subject.execute( new Runnable() { public void run() { //subject is 'bound' to the current thread now //any SecurityUtils.getSubject() calls in any //code called from here will work } }); //At this point, the Subject is no longer associated //with the current thread and everything is as it was before
案例十一(調用execute方法,參數為callable實例,實現subject的自動綁定與拆除):
Subject subject = //build or acquire subject
MyResult result = subject.execute( new Callable<MyResult>() { public MyResult call() throws Exception { //subject is 'bound' to the current thread now //any SecurityUtils.getSubject() calls in any //code called from here will work ... //finish logic as this Subject ... return myResult; } }); //At this point, the Subject is no longer associated //with the current thread and everything is as it was before
案例十二(spring遠程調用subject):
Subject.Builder builder = new Subject.Builder();
//populate the builder's attributes based on the incoming RemoteInvocation ... Subject subject = builder.buildSubject(); return subject.execute(new Callable() { public Object call() throws Exception { return invoke(invocation, targetObject); } });
線程池的清理:
Subject subject = new Subject.Builder()...
ThreadState threadState = new SubjectThreadState(subject); threadState.bind(); try { //execute work as the built Subject } finally { //ensure any state is cleaned so the thread won't be //corrupt in a reusable or pooled thread environment threadState.clear(); }
案例十三(callable):
Subject subject = new Subject.Builder()...
Callable work = //build/acquire a Callable instance. //associate the work with the built subject so SecurityUtils.getSubject() calls works properly: work = subject.associateWith(work); ExecutorService executorService = new java.util.concurrent.Executors.newCachedThreadPool(); //execute the work on a different thread as the built Subject: executor.execute(work);
案例十四(runable):
Subject subject = new Subject.Builder()...
Runnable work = //build/acquire a Runnable instance. //associate the work with the built subject so SecurityUtils.getSubject() calls works properly: work = subject.associateWith(work); Executor executor = new java.util.concurrent.Executors.newCachedThreadPool(); //execute the work on a different thread as the built Subject: executor.execute(work);