Shiro在1.0版本添加了一个新的特性,用于在特殊情景自定义subject。
2. Subject.Builder
Subject.Builder可以创建一个匿名,无session关联的Subject实例:
Subject subject = new Subject.Builder().buildSubject()
默认无参的Subject.Builder将使用应用中存在的SecurityManager。也可以指定 SecurityManager:
SecurityManager securityManager = //acquired from somewhere Subject subject = new Subject.Builder(securityManager).buildSubject();
Subject.Builder提供的其他方法都需要在buildSubject方法之前调用,假设有一个session ID,你想设置到Subject的session上:
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。需要注意的是,这样方式创建的Subject不会自动与应用线程绑定。如果想随处调用SecurityUtils.getSubject(),必须保证Subject与线程已绑定。
3. Thread Association(线程关联)
与线程绑定有三种方式:
1. Automatic Association -- 通过Subject.execute方法执行Callable或Runnable。
2. Manual Association -- 手动绑定或解除。
3. Different Thread -- 通过Subject.associateWith方法将Subject与Callable或Runnable绑定,并使用其他线程执行Callable或Runnable。
3.1 Automatic Association(自动关联)
如果想让Subject与线程暂时性绑定,可以直接通过Subject的execute方法执行Callable或Runnable,线程执行完毕,就解除绑定。
假如在应用启动时,有一些操作需要执行,当执行完后,希望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
也支持Callable:
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
这在框架开发中很有用。比如,Shiro支持Spring的远程调用,这可以保证远程调用被执行:
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); } });
3.2 Manual Association(手动关联)
在某些场景下,比如说使用Shiro进行框架级的集成开发时,就可能需要手动管理ThreadState(Shiro中保证Subject可以在线程执行间随处可用)。需要注意的是,要保证线程执行完后,清除ThreadState:
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(); }
3.3 A Different Thread(使用其他线程方式)
Subject.associateWith方法将Subject与Callable或Runnable绑定,在线程执行期间,Subject被挂起,直到线程执行完后,Subject才可用, 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);
使用Runnable的例子:
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);