首先,在系統中shiro提供交互的對象是Subject;因此先弄明白subject的生成和保存;
java
知道shiro是以filter的方式整合到系統中的,那麼就查看shiro的filter
redis
主要看AbstractShiroFilter的doFilterInternal方法就好了,裏面的一部分主要代碼數據庫
final Subject subject = createSubject(request, response);//這裏建立或者從緩存裏取subject //noinspection unchecked subject.execute(new Callable() { public Object call() throws Exception { updateSessionLastAccessTime(request, response);//這裏更新或者保存session executeChain(request, response, chain); return null; } });
因此得知每次被攔截都會生成或者提取subject;apache
createSubject(....)的主要調用代碼在DefaultSecurityManager類的createSubject(SubjectContext subjectContext)方法中;緩存
/** * This implementation functions as follows: * <p/> * <ol> * <li>Ensures the {@code SubjectContext} is as populated as it can be, using heuristics to acquire * data that may not have already been available to it (such as a referenced session or remembered principals).</li> * <li>Calls {@link #doCreateSubject(org.apache.shiro.subject.SubjectContext)} to actually perform the * {@code Subject} instance creation.</li> * <li>calls {@link #save(org.apache.shiro.subject.Subject) save(subject)} to ensure the constructed * {@code Subject}'s state is accessible for future requests/invocations if necessary.</li> * <li>returns the constructed {@code Subject} instance.</li> * </ol> * * @param subjectContext any data needed to direct how the Subject should be constructed. * @return the {@code Subject} instance reflecting the specified contextual data. * @see #ensureSecurityManager(org.apache.shiro.subject.SubjectContext) * @see #resolveSession(org.apache.shiro.subject.SubjectContext) * @see #resolvePrincipals(org.apache.shiro.subject.SubjectContext) * @see #doCreateSubject(org.apache.shiro.subject.SubjectContext) * @see #save(org.apache.shiro.subject.Subject) * @since 1.0 */ public Subject createSubject(SubjectContext subjectContext) { //create a copy so we don't modify the argument's backing map: SubjectContext context = copy(subjectContext);//上下文環境?其實就幾個屬性 //ensure that the context has a SecurityManager instance, and if not, add one: context = ensureSecurityManager(context); //Resolve an associated Session (usually based on a referenced session ID), and place it in the context before //sending to the SubjectFactory. The SubjectFactory should not need to know how to acquire sessions as the //process is often environment specific - better to shield the SF from these details: context = resolveSession(context);//關聯session的方法,若是配置session則會取緩存中的session //Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first //if possible before handing off to the SubjectFactory: context = resolvePrincipals(context);//關聯受權信息 Subject subject = doCreateSubject(context); //save this subject for future reference if necessary: //(this is needed here in case rememberMe principals were resolved and they need to be stored in the //session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation). //Added in 1.2: save(subject);//保存subject,subject是保存爲線程相關的,實際上是線程繼承相關,因此也就跟容器相關;而受權信息、認證信息也保存在session裏 return subject; }
shiro提供獲取subject的途徑是SecurityUtils.getSubject(),就會返回線程相關的subject。保存的容器被定義爲tomcat
private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();
-----------------------------------------------如下是吐槽-----------------------------------------------------session
項目要改成支持多節點的部署方式,以爲應該把shiro的session緩存在redis中就好了,因而用了http://www.oschina.net/p/shiro-redis(其實一直在用的)。把項目部署到兩個tomcat後,一直登錄驗證模塊就失效了,不是說不走shiro攔截了,這個確定是通過的。只是登錄的流程中兩臺tomcat都有接受到請求。調試的結果發如今一臺tomcat上成功登錄的信息,在另外一個tomcat上獲取爲空.....各類不明白,就這樣花了我兩天時間,,,,,,,沒搞定。。。嗯,還好有高手指點。問題是shiro-redis的提供了本地緩存。。。。。。。。。。。無語,,,無語中把shiro-redis的sessionDAO本身實現一個,本身實現的是直接從redis中獲取shiro的信息,不用什麼本地緩存。ui
------------------------------------------如下是待解決的問題--------------------------------------------------------this
在開啓session中發現,認證信息也是能夠緩存的,就是AuthenticationInfo也是能夠放入緩存中的,沒必要每次登錄都從數據庫中獲取數據進行認證。調試得知緩存認證是根據密碼匹配的;可是如何刷新還沒查看.net
------------------------------------------------05.07---------------------------------------------
如下來講明上面待解決的問題;
若是開啓了認證和受權的緩存.
AuthenticatingRealm.cacheAuthenticationInfoIfPossible方法緩存認證信息;
AuthorizingRealm.getAuthorizationInfo方法緩存和獲取受權信息;
注意認證或是受權緩存的key值是否有對象的hash值,若是有每次都保存同一個帳號的多個緩存值;
受權緩存的key是根據Principal類的toString獲得的