首先,在系统中shiro提供交互的对象是Subject;所以先弄明白subject的生成和保存;
知道shiro是以filter的方式整合到系统中的,那么就查看shiro的filter
主要看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;
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。保存的容器被定义为
private static final ThreadLocal<Map<Object, Object>> resources = new InheritableThreadLocalMap<Map<Object, Object>>();
-----------------------------------------------以下是吐槽-----------------------------------------------------
项目要改为支持多节点的部署方式,觉得应该把shiro的session缓存在redis中就行了,于是用了http://www.oschina.net/p/shiro-redis(其实一直在用的)。把项目部署到两个tomcat后,一直登陆验证模块就失效了,不是说不走shiro拦截了,这个肯定是经过的。只是登陆的流程中两台tomcat都有接受到请求。调试的结果发现在一台tomcat上成功登陆的信息,在另一个tomcat上获取为空.....各种不明白,就这样花了我两天时间,,,,,,,没搞定。。。嗯,还好有高手指点。问题是shiro-redis的提供了本地缓存。。。。。。。。。。。无语,,,无语中把shiro-redis的sessionDAO自己实现一个,自己实现的是直接从redis中获取shiro的信息,不用什么本地缓存。
------------------------------------------以下是待解决的问题--------------------------------------------------------
在开启session中发现,认证信息也是可以缓存的,就是AuthenticationInfo也是可以放入缓存中的,不必每次登陆都从数据库中获取数据进行认证。调试得知缓存认证是根据密码匹配的;但是如何刷新还没查看
------------------------------------------------05.07---------------------------------------------
以下来说明上面待解决的问题;
如果开启了认证和授权的缓存.
AuthenticatingRealm.cacheAuthenticationInfoIfPossible方法缓存认证信息;
AuthorizingRealm.getAuthorizationInfo方法缓存和获取授权信息;
注意认证或是授权缓存的key值是否有对象的hash值,如果有每次都保存同一个账号的多个缓存值;
授权缓存的key是根据Principal类的toString得到的