Apache Shiro 会话与主体状态的关系

来源:互联网 发布:网络接线盒接法图解 编辑:程序博客网 时间:2024/06/06 02:12

有状态的应用程序

默认情况下Shiro SecurityManager 实例会使用一个Subject Session存储Subject 的身份IDPrincipalCollection)和验证状态(subject.isAuthenticated())。这通常发生在一个Subject 登录后或当一个Subject的身份ID 通过Remember 服务后。

使用这种默认方式的好处是:

l 任何应用都可通过Session ID来关联请求/调用/消息,并且这是关联用户所必需的。例如,使用Subject.Builder来获取相关的Subject

Serializable sessionId = //get from the inbound request or remote method invocation payload

Subject requestSubject = new Subject.Builder().sessionId(sessionId).buildSubject();

l 任何"RememberMe"身份能够在第一次访问时就能持久化到会话的初始请求。这确保了Subject 被记住的身份可以跨请求保存而不需要反序列化及将它解释到每个请求。例如,在一个Web 应用程序中,没有必要去读取每一个请求的加密RememberMe Cookie,如果该身份在会话中是已知的。这可是一个很好的性能提升。

 无状态的应用程序

虽然上述的默认策略对于大多数应用程序而言是很好的(通常是可取的),但这对于无状态的应用程序来说是不合适的。许多无状态的架构规定在请求中不能存在持久状态,这种情况下的Sessions 不会被允许(一个会话其本质代表了持久状态)。

但这一要求带来一个便利的代价——Subject 状态不能跨请求保留。这意味着有这一要求的应用程序必须确保Subject状态可以在每一个请求中以其他的方式代表。

这几乎总是通过验证每个由应用程序处理的请求/调用/消息来完成的。例如,大多数无状态Web 应用程序通常支持这一点通过执行HTTP 基本验证,允许浏览器验证每一个代表最终用户的请求。远程或消息框架必须确保Subject 的身份和凭证连接到每一个调用或消息的有效载荷,通常是由框架代码执行。

在无状态应用中需要禁用将Subject状态持久化到会话,可通过如下配置实现:

[main]

securityManager.subjectDAO.sessionStorageEvaluator.sessionStorageEnabled = false

需要注意的是,禁用使用Sessions 作为存储策略的实现,但它没有完全地禁用Sessions。如果你的任何代码显式地调用subject.getSession()subject.getSession(true),一个session 仍然会被创建。

 混合方法

但,如果你想使用混合的方法呢?如果某些对象应该有会话而某些没有?这种混合法方法能够给许多应用程序带来好处。例如:

l 也许human Subject(如Web 浏览器用户)由于上面提供的好处能够使用Session

l 也许non-human Subject(如API 客户端或第三方应用程序)不应该创建session 由于它们与软件的交互可能会间歇或不稳定。

l 也许所有某种确定类型的Subject 或从某一确定位置访问系统的应该将状态保持在会话中,但所有其他的不应该。

如果你需要这个混合方法,你可以实现一个SessionStorageEvaluator接口,告诉Shiro 哪个Subject 支持会话存储。

该接口只有一个方法:

public interface SessionStorageEvaluator {

  public boolean isSessionStorageEnabled(Subject subject);

}

例如,在Web 应用程序中,如果该决定必须基于当前ServletRequest 中的数据,你可以获取该request 或该response,因为运行时的Subjce 实例实际上就是一个WebSubject 实例:

public boolean isSessionStorageEnabled(Subject subject) {

  boolean enabled = false;

  if(WebUtils.isWeb(Subject)) {

    HttpServletRequest request = WebUtils.getHttpRequest(subject);

    //set 'enabled' based on the current request.

  } else {

    //not a web request - maybe a RMI or daemon invocation?

    //set 'enabled' another way …

  }

  return enabled;

}

在你实现了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 异常:

l httpServletRequest.getSession()

l httpServletRequest.getSession(true)

l subject.getSession()

l subject.getSession(true)

如果一个Subject 在访问noSessionCreation-protected-URL 之前已经有一个会话,则上述的四种调用仍然会如预期般工作。

最后,在所有情况下,下面的调用将始终被允许:

l httpServletRequest.getSession(false)

l subject.getSession(false)

原创粉丝点击