有状态的应用程序
默认情况下Shiro的SecurityManager实例会使用一个Subject的Session存储Subject的身份ID(PrincipalCollection)和验证状态(subject.isAuthenticated())。这通常发生在一个Subject登录后或当一个Subject的身份ID通过Remember服务后。
使用这种默认方式的好处是:
l任何应用都可通过SessionID来关联请求/调用/消息,并且这是关联用户所必需的。例如,使用Subject.Builder来获取相关的Subject
SerializablesessionId=//getfromtheinboundrequestorremotemethodinvocationpayload SubjectrequestSubject=newSubject.Builder().sessionId(sessionId).buildSubject(); |
l任何"RememberMe"身份能够在第一次访问时就能持久化到会话的初始请求。这确保了Subject被记住的身份可以跨请求保存而不需要反序列化及将它解释到每个请求。例如,在一个Web应用程序中,没有必要去读取每一个请求的加密RememberMeCookie,如果该身份在会话中是已知的。这可是一个很好的性能提升。
无状态的应用程序
虽然上述的默认策略对于大多数应用程序而言是很好的(通常是可取的),但这对于无状态的应用程序来说是不合适的。许多无状态的架构规定在请求中不能存在持久状态,这种情况下的Sessions不会被允许(一个会话其本质代表了持久状态)。
但这一要求带来一个便利的代价——Subject状态不能跨请求保留。这意味着有这一要求的应用程序必须确保Subject状态可以在每一个请求中以其他的方式代表。
这几乎总是通过验证每个由应用程序处理的请求/调用/消息来完成的。例如,大多数无状态Web应用程序通常支持这一点通过执行HTTP基本验证,允许浏览器验证每一个代表最终用户的请求。远程或消息框架必须确保Subject的身份和凭证连接到每一个调用或消息的有效载荷,通常是由框架代码执行。
在无状态应用中需要禁用将Subject状态持久化到会话,可通过如下配置实现:
[main] … securityManager.subjectDAO.sessionStorageEvaluator.sessionStorageEnabled=false |
需要注意的是,禁用使用Sessions作为存储策略的实现,但它没有完全地禁用Sessions。如果你的任何代码显式地调用subject.getSession()或subject.getSession(true),一个session仍然会被创建。
混合方法
但,如果你想使用混合的方法呢?如果某些对象应该有会话而某些没有?这种混合法方法能够给许多应用程序带来好处。例如:
l也许humanSubject(如Web浏览器用户)由于上面提供的好处能够使用Session。
l也许non-humanSubject(如API客户端或第三方应用程序)不应该创建session由于它们与软件的交互可能会间歇或不稳定。
l也许所有某种确定类型的Subject或从某一确定位置访问系统的应该将状态保持在会话中,但所有其他的不应该。
如果你需要这个混合方法,你可以实现一个SessionStorageEvaluator接口,告诉Shiro哪个Subject支持会话存储。
该接口只有一个方法:
publicinterfaceSessionStorageEvaluator{ publicbooleanisSessionStorageEnabled(Subjectsubject); } |
例如,在Web应用程序中,如果该决定必须基于当前ServletRequest中的数据,你可以获取该request或该response,因为运行时的Subjce实例实际上就是一个WebSubject实例:
publicbooleanisSessionStorageEnabled(Subjectsubject){ booleanenabled=false; if(WebUtils.isWeb(Subject)){ HttpServletRequestrequest=WebUtils.getHttpRequest(subject); //set'enabled'basedonthecurrentrequest. }else{ //notawebrequest-maybeaRMIordaemoninvocation? //set'enabled'anotherway… } returnenabled; } |
在你实现了SessionStorageEvaluator接口后,你可以在shiro.ini中配置它:
[main] … sessionStorageEvaluator=com.mycompany.shiro.subject.mgt.MySessionStorageEvaluator securityManager.subjectDAO.sessionStorageEvaluator=$sessionStorageEvaluator ... |
WEB应用
通常Web应用程序希望在每一个请求的基础上容易地启用或禁用会话的创建,不管是哪个Subject正在执行请求。这经常在支持REST及Messaging/RMI构架上使用来产生很好的效果。例如,也许正常的终端用户(使用浏览器的人)被允许创建和使用会话,但远程的API客户端使用REST或SOAP,不该拥有会话(因为它们在每一个请求上验证,常见REST/SOAP体系结构)。
为了支持这种hybrid/per-request的能力,noSessionCreation过滤器被添加到Shiro的默认为Web应用程序启用的“池”。该过滤器将会阻止在请求期间创建新的会话来保证无状态的体验。在shiro.ini的[urls]项中,你通常定义该过滤器在所有其它过滤器之前来确保会话永远不会被使用。
例如:
[urls] … /rest/**=noSessionCreation,authcBasic,... |
这个过滤器允许现有会话的任何会话操作,但不允许在过滤的请求创建新的会话。也就是说,一个请求或没有会话存在的Subject调用下面四个方法中的任何一个时,将会自动地触发一个DisabledSessionException异常:
lhttpServletRequest.getSession()
lhttpServletRequest.getSession(true)
lsubject.getSession()
lsubject.getSession(true)
如果一个Subject在访问noSessionCreation-protected-URL之前已经有一个会话,则上述的四种调用仍然会如预期般工作。
最后,在所有情况下,下面的调用将始终被允许:
lhttpServletRequest.getSession(false)
l subject.getSession(false)