大家好,今天来为大家分享共享源码分享网站怎么做的啊的一些知识点,和有什么源码分享网站的问题解析,大家要是都明白,那么可以忽略,如果不太清楚的话可以看看本篇文章,相信很大概率可以解决您的问题,接下来我们就一起来看看吧!
为何需要session共享
如果是单机应用,那么谈不上session共享,session放哪都无所谓,不在乎放到默认的servlet容器中,还是抽出来放到单独的地方;
也就是说session共享是针对集群(或分布式、或分布式集群)的;如果不做session共享,仍然采用默认的方式(session存放到默认的servlet容器),当我们的应用是以集群的方式发布的时候,同个用户的请求会被分发到不同的集群节点(分发依赖具体的负载均衡规则),那么每个处理同个用户请求的节点都会重新生成该用户的session,这些session之间是毫无关联的。那么同个用户的请求会被当成多个不同用户的请求,这肯定是不行的。
如何实现session共享
实现方式其实有很多,甚至可以不做session共享,具体有哪些,大家自行去查资料。本文提供一种方式:redis实现session共享,就是将session从servlet容器抽出来,放到redis中存储,所有集群节点都从redis中对session进行操作。
回到顶部
SessionDAO
SessionDAO其实是用于session持久化的,但里面有缓存部分,具体细节我们往下看
shiro已有SessionDAO的实现如下
SessionDAO接口提供的方法如下
packageorg.apache.shiro.session.mgt.eis;\nimportorg.apache.shiro.session.Session;\nimportorg.apache.shiro.session.UnknownSessionException;\nimportjava.io.Serializable;\nimportjava.util.Collection;\n/**\n*从EIS操作session的规范(EIS:例如关系型数据库,文件系统,持久化缓存等等,具体依赖DAO实现)\n*提供了典型的CRUD的方法:create,readSession,update,delete\n*/\npublicinterfaceSessionDAO{\n/**\n*插入一个新的sesion记录到EIS\n*/\nSerializablecreate(Sessionsession);\n/**\n*根据会话ID获取会话\n*/\nSessionreadSession(SerializablesessionId)throwsUnknownSessionException;\n/**\n*更新session;如更新session最后访问时间/停止会话/设置超时时间/设置移除属性等会调用\n*/\nvoidupdate(Sessionsession)throwsUnknownSessionException;\n/**\n*删除session;当会话过期/会话停止(如用户退出时)会调用\n*/\nvoiddelete(Sessionsession);\n/**\n*获取当前所有活跃session,所有状态不是stopped/expired的session\n*如果用户量多此方法影响性能\n*/\nCollection<Session>getActiveSessions();\n}\n
SessionDAO给出了从持久层(一般而言是关系型数据库)操作session的标准。
AbstractSessionDAO提供了SessionDAO的基本实现,如下
packageorg.apache.shiro.session.mgt.eis;\nimportorg.apache.shiro.session.Session;\nimportorg.apache.shiro.session.UnknownSessionException;\nimportorg.apache.shiro.session.mgt.SimpleSession;\nimportjava.io.Serializable;\n/**\n*SessionDAO的抽象实现,在会话创建和读取时做一些健全性检查,并在需要时允许可插入的会话ID生成策略.\n*SessionDAO的update和delete则留给子类来实现\n*EIS需要子类自己实现\n*/\npublicabstractclassAbstractSessionDAOimplementsSessionDAO{\n/**\n*sessionId生成器\n*/\nprivateSessionIdGeneratorsessionIdGenerator;\npublicAbstractSessionDAO(){\nthis.sessionIdGenerator=newJavaUuidSessionIdGenerator();//指定JavaUuidSessionIdGenerator为默认sessionId生成器\n}\n/**\n*获取sessionId生成器\n*/\npublicSessionIdGeneratorgetSessionIdGenerator(){\nreturnsessionIdGenerator;\n}\n/**\n*设置sessionId生成器\n*/\npublicvoidsetSessionIdGenerator(SessionIdGeneratorsessionIdGenerator){\nthis.sessionIdGenerator=sessionIdGenerator;\n}\n/**\n*生成一个新的sessionId,并将它应用到session实例\n*/\nprotectedSerializablegenerateSessionId(Sessionsession){\nif(this.sessionIdGenerator==null){\nStringmsg=”sessionIdGeneratorattributehasnotbeenconfigured.”;\nthrownewIllegalStateException(msg);\n}\nreturnthis.sessionIdGenerator.generateId(session);\n}\n/**\n*SessionDAO中create实现;将创建的sesion保存到EIS.\n*子类doCreate方法的代理,具体的细节委托给了子类的doCreate方法\n*/\npublicSerializablecreate(Sessionsession){\nSerializablesessionId=doCreate(session);\nverifySessionId(sessionId);\nreturnsessionId;\n}\n/**\n*保证从doCreate返回的sessionId不是null,并且不是已经存在的.\n*目前只实现了null校验,是否已存在是没有校验的,可能shiro的开发者会在后续补上吧.\n*/\nprivatevoidverifySessionId(SerializablesessionId){\nif(sessionId==null){\nStringmsg=”sessionIdreturnedfromdoCreateimplementationisnull.Pleaseverifytheimplementation.”;\nthrownewIllegalStateException(msg);\n}\n}\n/**\n*分配sessionId给session实例\n*/\nprotectedvoidassignSessionId(Sessionsession,SerializablesessionId){\n((SimpleSession)session).setId(sessionId);\n}\n/**\n*子类通过实现此方法来持久化Session实例到EIS.\n*/\nprotectedabstractSerializabledoCreate(Sessionsession);\n/**\n*SessionDAO中readSession实现;通过sessionId从EIS获取session对象.\n*子类doReadSession方法的代理,具体的获取细节委托给了子类的doReadSession方法.\n*/\npublicSessionreadSession(SerializablesessionId)throwsUnknownSessionException{\nSessions=doReadSession(sessionId);\nif(s==null){\nthrownewUnknownSessionException(“Thereisnosessionwithid[“+sessionId+”]”);\n}\nreturns;\n}\n/**\n*子类通过实现此方法从EIS获取session实例\n*/\nprotectedabstractSessiondoReadSession(SerializablesessionId);\n}\n
SessionDao的基本实现,实现了SessionDao的create、readSession(具体还是依赖AbstractSessionDAO子类的doCreate、doReadSession实现);同时加入了自己的sessionId生成器,负责sessionId的操作。
CachingSessionDAO提供了session缓存的功能,如下
packageorg.apache.shiro.session.mgt.eis;\nimportorg.apache.shiro.cache.Cache;\nimportorg.apache.shiro.cache.CacheManager;\nimportorg.apache.shiro.cache.CacheManagerAware;\nimportorg.apache.shiro.session.Session;\nimportorg.apache.shiro.session.UnknownSessionException;\nimportorg.apache.shiro.session.mgt.ValidatingSession;\nimportjava.io.Serializable;\nimportjava.util.Collection;\nimportjava.util.Collections;\n/**\n*应用层与持久层(EIS,如关系型数据库、文件系统、NOSQL)之间的缓存层实现\n*缓存着所有激活状态的session\n*实现了CacheManagerAware,会在shiro加载的过程中调用此对象的setCacheManager方法\n*/\npublicabstractclassCachingSessionDAOextendsAbstractSessionDAOimplementsCacheManagerAware{\n/**\n*激活状态的sesion的默认缓存名\n*/\npublicstaticfinalStringACTIVE_SESSION_CACHE_NAME=”shiro-activeSessionCache”;\n/**\n*缓存管理器,用来获取session缓存\n*/\nprivateCacheManagercacheManager;\n/**\n*用来缓存session的缓存实例\n*/\nprivateCache<Serializable,Session>activeSessions;\n/**\n*session缓存名,默认是ACTIVE_SESSION_CACHE_NAME.\n*/\nprivateStringactiveSessionsCacheName=ACTIVE_SESSION_CACHE_NAME;\npublicCachingSessionDAO(){\n}\n/**\n*设置缓存管理器\n*/\npublicvoidsetCacheManager(CacheManagercacheManager){\nthis.cacheManager=cacheManager;\n}\n/**\n*获取缓存管理器\n*/\npublicCacheManagergetCacheManager(){\nreturncacheManager;\n}\n/**\n*获取缓存实例的名称,也就是获取activeSessionsCacheName的值\n*/\npublicStringgetActiveSessionsCacheName(){\nreturnactiveSessionsCacheName;\n}\n/**\n*设置缓存实例的名称,也就是设置activeSessionsCacheName的值\n*/\npublicvoidsetActiveSessionsCacheName(StringactiveSessionsCacheName){\nthis.activeSessionsCacheName=activeSessionsCacheName;\n}\n/**\n*获取缓存实例\n*/\npublicCache<Serializable,Session>getActiveSessionsCache(){\nreturnthis.activeSessions;\n}\n/**\n*设置缓存实例\n*/\npublicvoidsetActiveSessionsCache(Cache<Serializable,Session>cache){\nthis.activeSessions=cache;\n}\n/**\n*获取缓存实例\n*注意:不会返回non-null值\n*\n*@returntheactivesessionscacheinstance.\n*/\nprivateCache<Serializable,Session>getActiveSessionsCacheLazy(){\nif(this.activeSessions==null){\nthis.activeSessions=createActiveSessionsCache();\n}\nreturnactiveSessions;\n}\n/**\n*创建缓存实例\n*/\nprotectedCache<Serializable,Session>createActiveSessionsCache(){\nCache<Serializable,Session>cache=null;\nCacheManagermgr=getCacheManager();\nif(mgr!=null){\nStringname=getActiveSessionsCacheName();\ncache=mgr.getCache(name);\n}\nreturncache;\n}\n/**\n*AbstractSessionDAO中create的重写\n*调用父类(AbstractSessionDAO)的create方法,然后将session缓存起来\n*返回sessionId\n*/\npublicSerializablecreate(Sessionsession){\nSerializablesessionId=super.create(session);//调用父类的create方法\ncache(session,sessionId);//以sessionId作为key缓存session\nreturnsessionId;\n}\n/**\n*从缓存中获取session;若sessionId为null,则返回null\n*/\nprotectedSessiongetCachedSession(SerializablesessionId){\nSessioncached=null;\nif(sessionId!=null){\nCache<Serializable,Session>cache=getActiveSessionsCacheLazy();\nif(cache!=null){\ncached=getCachedSession(sessionId,cache);\n}\n}\nreturncached;\n}\n/**\n*从缓存中获取session\n*/\nprotectedSessiongetCachedSession(SerializablesessionId,Cache<Serializable,Session>cache){\nreturncache.get(sessionId);\n}\n/**\n*缓存session,以sessionId作为key\n*/\nprotectedvoidcache(Sessionsession,SerializablesessionId){\nif(session==null||sessionId==null){\nreturn;\n}\nCache<Serializable,Session>cache=getActiveSessionsCacheLazy();\nif(cache==null){\nreturn;\n}\ncache(session,sessionId,cache);\n}\nprotectedvoidcache(Sessionsession,SerializablesessionId,Cache<Serializable,Session>cache){\ncache.put(sessionId,session);\n}\n/**\n*AbstractSessionDAO中readSession的重写\n*先从缓存中获取,若没有则调用父类的readSession方法获取session\n*/\npublicSessionreadSession(SerializablesessionId)throwsUnknownSessionException{\nSessions=getCachedSession(sessionId);//从缓存中获取\nif(s==null){\ns=super.readSession(sessionId);//调用父类readSession方法获取\n}\nreturns;\n}\n/**\n*SessionDAO中update的实现\n*更新session的状态\n*/\npublicvoidupdate(Sessionsession)throwsUnknownSessionException{\ndoUpdate(session);//更新EIS中的session\nif(sessioninstanceofValidatingSession){\nif(((ValidatingSession)session).isValid()){\ncache(session,session.getId());//更新缓存中的session\n}else{\nuncache(session);//移除缓存中的sesson\n}\n}else{\ncache(session,session.getId());\n}\n}\n/**\n*由子类去实现,持久化session到EIS\n*/\nprotectedabstractvoiddoUpdate(Sessionsession);\n/**\n*SessionDAO中delete的实现\n*删除session\n*/\npublicvoiddelete(Sessionsession){\nuncache(session);//从缓存中移除\ndoDelete(session);//从EIS中删除\n}\n/**\n*由子类去实现,从EIS中删除session\n*/\nprotectedabstractvoiddoDelete(Sessionsession);\n/**\n*从缓存中移除指定的session\n*/\nprotectedvoiduncache(Sessionsession){\nif(session==null){\nreturn;\n}\nSerializableid=session.getId();\nif(id==null){\nreturn;\n}\nCache<Serializable,Session>cache=getActiveSessionsCacheLazy();\nif(cache!=null){\ncache.remove(id);\n}\n}\n/**\n*SessionDAO中getActiveSessions的实现\n*获取所有的存活的session\n*/\npublicCollection<Session>getActiveSessions(){\nCache<Serializable,Session>cache=getActiveSessionsCacheLazy();\nif(cache!=null){\nreturncache.values();\n}else{\nreturnCollections.emptySet();\n}\n}\n}\n
是应用层与持久化层之间的缓存层,不用频繁请求持久化层以提升效率。重写了AbstractSessionDAO中的create、readSession方法,实现了SessionDAO中的update、delete、getActiveSessions方法,预留doUpdate和doDelele给子类去实现(doXXX方法操作的是持久层)
MemorySessionDAO,SessionDAO的简单内存实现,如下
packageorg.apache.shiro.session.mgt.eis;\nimportorg.apache.shiro.session.Session;\nimportorg.apache.shiro.session.UnknownSessionException;\nimportorg.apache.shiro.util.CollectionUtils;\nimportorg.slf4j.Logger;\nimportorg.slf4j.LoggerFactory;\nimportjava.io.Serializable;\nimportjava.util.Collection;\nimportjava.util.Collections;\nimportjava.util.concurrent.ConcurrentHashMap;\nimportjava.util.concurrent.ConcurrentMap;\n/**\n*基于内存的SessionDao的简单实现,所有的session存在ConcurrentMap中\n*DefaultSessionManager默认用的MemorySessionDAO\n*/\npublicclassMemorySessionDAOextendsAbstractSessionDAO{\nprivatestaticfinalLoggerlog=LoggerFactory.getLogger(MemorySessionDAO.class);\nprivateConcurrentMap<Serializable,Session>sessions;//存放session的容器\npublicMemorySessionDAO(){\nthis.sessions=newConcurrentHashMap<Serializable,Session>();\n}\n//AbstractSessionDAO中doCreate的重写;将session存入sessions\nprotectedSerializabledoCreate(Sessionsession){\nSerializablesessionId=generateSessionId(session);//生成sessionId\nassignSessionId(session,sessionId);//将sessionId赋值到session\nstoreSession(sessionId,session);//存储session到sessions\nreturnsessionId;\n}\n//存储session到sessions\nprotectedSessionstoreSession(Serializableid,Sessionsession){\nif(id==null){\nthrownewNullPointerException(“idargumentcannotbenull.”);\n}\nreturnsessions.putIfAbsent(id,session);\n}\n//AbstractSessionDAO中doReadSession的重写;从sessions中获取session\nprotectedSessiondoReadSession(SerializablesessionId){\nreturnsessions.get(sessionId);\n}\n//SessionDAO中update的实现;更新sessions中指定的session\npublicvoidupdate(Sessionsession)throwsUnknownSessionException{\nstoreSession(session.getId(),session);\n}\n//SessionDAO中delete的实现;从sessions中移除指定的session\npublicvoiddelete(Sessionsession){\nif(session==null){\nthrownewNullPointerException(“sessionargumentcannotbenull.”);\n}\nSerializableid=session.getId();\nif(id!=null){\nsessions.remove(id);\n}\n}\n//SessionDAO中SessionDAO中delete的实现的实现;获取sessions中全部session\npublicCollection<Session>SessionDAO中delete的实现(){\nCollection<Session>values=sessions.values();\nif(CollectionUtils.isEmpty(values)){\nreturnCollections.emptySet();\n}else{\nreturnCollections.unmodifiableCollection(values);\n}\n}\n}\n
将session保存在内存中,存储结构是ConcurrentHashMap;项目中基本不用,即使我们不实现自己的SessionDAO,一般用的也是EnterpriseCacheSessionDAO。
EnterpriseCacheSessionDAO,提供了缓存功能的session维护,如下
packageorg.apache.shiro.session.mgt.eis;\nimportorg.apache.shiro.cache.AbstractCacheManager;\nimportorg.apache.shiro.cache.Cache;\nimportorg.apache.shiro.cache.CacheException;\nimportorg.apache.shiro.cache.MapCache;\nimportorg.apache.shiro.session.Session;\nimportjava.io.Serializable;\nimportjava.util.concurrent.ConcurrentHashMap;\npublicclassEnterpriseCacheSessionDAOextendsCachingSessionDAO{\npublicEnterpriseCacheSessionDAO(){\n\n//设置默认缓存器,并实例化MapCache作为cache实例\nsetCacheManager(newAbstractCacheManager(){\n@Override\nprotectedCache<Serializable,Session>createCache(Stringname)throwsCacheException{\nreturnnewMapCache<Serializable,Session>(name,newConcurrentHashMap<Serializable,Session>());\n}\n});\n}\n//AbstractSessionDAO中doCreate的重写;\nprotectedSerializabledoCreate(Sessionsession){\nSerializablesessionId=generateSessionId(session);\nassignSessionId(session,sessionId);\nreturnsessionId;\n}\n//AbstractSessionDAO中doReadSession的重写\nprotectedSessiondoReadSession(SerializablesessionId){\nreturnnull;//shouldneverexecutebecausethisimplementationreliesonparentclasstoaccesscache,which\n//iswhereallsessionsreside-itisthecacheimplementationthatdeterminesifthe\n//cacheismemoryonlyordisk-persistent,etc.\n}\n//CachingSessionDAO中doUpdate的重写\nprotectedvoiddoUpdate(Sessionsession){\n//doesnothing-parentclasspersiststocache.\n}\n//CachingSessionDAO中doDelete的重写\nprotectedvoiddoDelete(Sessionsession){\n//doesnothing-parentclassremovesfromcache.\n}\n}\n
设置了默认的缓存管理器(AbstractCacheManager)和默认的缓存实例(MapCache),实现了缓存效果。从父类继承的持久化操作方法(doXXX)都是空实现,也就说EnterpriseCacheSessionDAO是没有实现持久化操作的,仅仅只是简单的提供了缓存实现。当然我们可以继承EnterpriseCacheSessionDAO,重写doXXX方法来实现持久化操作。
总结下:SessionDAO定义了从持久层操作session的标准;AbstractSessionDAO提供了SessionDAO的基础实现,如生成会话ID等;CachingSessionDAO提供了对开发者透明的session缓存的功能,只需要设置相应的CacheManager即可;MemorySessionDAO直接在内存中进行session维护;而EnterpriseCacheSessionDAO提供了缓存功能的session维护,默认情况下使用MapCache实现,内部使用ConcurrentHashMap保存缓存的会话。因为shiro不知道我们需要将session持久化到哪里(关系型数据库,还是文件系统),所以只提供了MemorySessionDAO持久化到内存(听起来怪怪的,内存中能说成持久层吗)
shirosession共享
共享实现
shiro的session共享其实是比较简单的,重写CacheManager,将其操作指向我们的redis,然后实现我们自己的CachingSessionDAO定制缓存操作和缓存持久化。
自定义CacheManager
ShiroRedisCacheManager
packagecom.lee.shiro.config;\nimportorg.apache.shiro.cache.Cache;\nimportorg.apache.shiro.cache.CacheException;\nimportorg.apache.shiro.cache.CacheManager;\nimportorg.springframework.beans.factory.annotation.Autowired;\nimportorg.springframework.stereotype.Component;\n@Component\npublicclassShiroRedisCacheManagerimplementsCacheManager{\n@Autowired\nprivateCacheshiroRedisCache;\n@Override\npublic<K,V>Cache<K,V>getCache(Strings)throwsCacheException{\nreturnshiroRedisCache;\n}\n}\n
ShiroRedisCache
packagecom.lee.shiro.config;\nimportorg.apache.shiro.cache.Cache;\nimportorg.apache.shiro.cache.CacheException;\nimportorg.springframework.beans.factory.annotation.Autowired;\nimportorg.springframework.beans.factory.annotation.Value;\nimportorg.springframework.data.redis.core.RedisTemplate;\nimportorg.springframework.stereotype.Component;\nimportjava.util.Collection;\nimportjava.util.Set;\nimportjava.util.concurrent.TimeUnit;\n@Component\npublicclassShiroRedisCache<K,V>implementsCache<K,V>{\n@Autowired\nprivateRedisTemplate<K,V>redisTemplate;\n@Value(“${spring.redis.expireTime}”)\nprivatelongexpireTime;\n@Override\npublicVget(Kk)throwsCacheException{\nreturnredisTemplate.opsForValue().get(k);\n}\n@Override\npublicVput(Kk,Vv)throwsCacheException{\nredisTemplate.opsForValue().set(k,v,expireTime,TimeUnit.SECONDS);\nreturnnull;\n}\n@Override\npublicVremove(Kk)throwsCacheException{\nVv=redisTemplate.opsForValue().get(k);\nredisTemplate.opsForValue().getOperations().delete(k);\nreturnv;\n}\n@Override\npublicvoidclear()throwsCacheException{\n}\n@Override\npublicintsize(){\nreturn0;\n}\n@Override\npublicSet<K>keys(){\nreturnnull;\n}\n@Override\npublicCollection<V>values(){\nreturnnull;\n}\n}\n
自定义CachingSessionDAO
继承EnterpriseCacheSessionDAO,然后重新设置其CacheManager(替换掉默认的内存缓存器),这样也可以实现我们的自定义CachingSessionDAO,但是这是优选吗;如若我们实现持久化,继承EnterpriseCacheSessionDAO是优选,但如果只是实现session缓存,那么CachingSessionDAO是优选,自定义更灵活。那么我们还是继承CachingSessionDAO来实现我们的自定义CachingSessionDAO
ShiroSessionDAO
packagecom.lee.shiro.config;\nimportorg.apache.shiro.session.Session;\nimportorg.apache.shiro.session.mgt.eis.CachingSessionDAO;\nimportorg.springframework.stereotype.Component;\nimportjava.io.Serializable;\n@Component\npublicclassShiroSessionDAOextendsCachingSessionDAO{\n@Override\nprotectedvoiddoUpdate(Sessionsession){\n}\n@Override\nprotectedvoiddoDelete(Sessionsession){\n}\n@Override\nprotectedSerializabledoCreate(Sessionsession){\n//这里绑定sessionId到session,必须要有\nSerializablesessionId=generateSessionId(session);\nassignSessionId(session,sessionId);\nreturnsessionId;\n}\n@Override\nprotectedSessiondoReadSession(SerializablesessionId){\nreturnnull;\n}\n}\n
最后将ShiroSessionDAO实例赋值给SessionManager实例,再讲SessionManager实例赋值给SecurityManager实例即可
源码解析
底层还是利用Filter+HttpServletRequestWrapper将对session的操作接入到自己的实现中来,而不走默认的servlet容器,这样对session的操作完全由我们自己掌握。
shiro的session创建中其实讲到了shiro中对session操作的基本流程,这里不再赘述,没看的朋友可以先去看看再回过头来看这篇。本文只讲shiro中,如何将一个请求的session接入到自己的实现中来的;shiro中有很多默认的filter,我会单独开一篇来讲shiro的filter,这篇我们先不纠结这些filter。
OncePerRequestFilter中doFilter方法如下
publicfinalvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainfilterChain)\nthrowsServletException,IOException{\nStringalreadyFilteredAttributeName=getAlreadyFilteredAttributeName();\nif(request.getAttribute(alreadyFilteredAttributeName)!=null){//当前filter已经执行过了,进行下一个filter\nlog.trace(“Filter'{}’alreadyexecuted.Proceedingwithoutinvokingthisfilter.”,getName());\nfilterChain.doFilter(request,response);\n}else//noinspectiondeprecation\nif(/*addedin1.2:*/!isEnabled(request,response)||\n/*retainbackwardscompatibility:*/shouldNotFilter(request)){//当前filter未被启用或忽略此filter,则进行下一个filter;shouldNotFilter已经被废弃了\nlog.debug(“Filter'{}’isnotenabledforthecurrentrequest.Proceedingwithoutinvokingthisfilter.”,\ngetName());\nfilterChain.doFilter(request,response);\n}else{\n//Doinvokethisfilter…\nlog.trace(“Filter'{}’notyetexecuted.Executingnow.”,getName());\nrequest.setAttribute(alreadyFilteredAttributeName,Boolean.TRUE);\ntry{\n//执行当前filter\ndoFilterInternal(request,response,filterChain);\n}finally{\n//一旦请求完成,我们清除当前filter的”已经过滤”的状态\nrequest.removeAttribute(alreadyFilteredAttributeName);\n}\n}\n}\n\n
上图中,我可以看到AbstractShiroFilter的doFilterInternal放中将request封装成了shiro自定义的ShiroHttpServletRequest,将response也封装成了shiro自定义的ShiroHttpServletResponse。既然Filter中将request封装了ShiroHttpServletRequest,那么到我们应用的request就是ShiroHttpServletRequest类型,也就是说我们对session的操作最终都是由shiro完成的,而不是默认的servlet容器。
另外补充一点,shiro的session创建不是懒创建的。servlet容器中的session创建是第一次请求session(第一调用request.getSession())时才创建。shiro的session创建如下图
此时,还没登录,但是subject、session已经创建了,只是subject的认证状态为false,说明还没进行登录认证的。至于session创建过程已经保存到redis的流程需要大家自行去跟,或者阅读我之前的博文
总结
1、当以集群方式对外提供服务的时候,不做session共享也是可以的
可以通过ip_hash的机制将同个ip的请求定向到同一台后端,这样保证用户的请求始终是同一台服务处理,与单机应用基本一致了;但这有很多方面的缺陷(具体就不详说了),不推荐使用。
2、servlet容器之间做session同步也是可以实现session共享的
一个servlet容器生成session,其他节点的servlet容器从此servlet容器进行session同步,以达到session信息一致。这个也不推荐,某个时间点会有session不一致的问题,毕竟同步过程受到各方面的影响,不能保证session实时一致。
3、session共享实现的原理其实都是一样的,都是filter+HttpServletRequestWrapper,只是实现细节会有所区别;有兴趣的可以看下spring-session的实现细节。
4、如果我们采用的spring集成shiro,其实可以将缓存管理器交由spring管理,相当于由spring统一管理缓存。
5、shiro的CacheManager不只是管理session缓存,还管理着身份认证缓存、授权缓存,shiro的缓存都是CacheManager管理。但是身份认证缓存默认是关闭的,个人也不推荐开启。
6、shiro的session创建时机是在登录认证之前,而不是第一次调用getSession()时。
好了,关于共享源码分享网站怎么做的啊和有什么源码分享网站的问题到这里结束啦,希望可以解决您的问题哈!