概述
SecurityContextPersistenceFilter有两个主要任务:
- 在请求到达时处理之前,从SecurityContextRepository中获取安全上下文信息填充到SecurityContextHolder;
- 在请求处理结束后返回响应时,将SecurityContextHolder中的安全上下文信息保存回SecurityContextRepository,并清空SecurityContextHolder。
通过SecurityContextPersistenceFilter的这种机制,在整个请求处理过程中,开发人员都可以通过使用SecurityContextHolder获取当前访问用户的安全上下文信息。
缺省情况下,SecurityContextPersistenceFilter使用的SecurityContextRepository是HttpSessionSecurityContextRepository,也就是将安全上下文的信息保存在用户的会话中。
为了解决不同Serlvet容器上,尤其是weblogic上的兼容性问题,此Filter必须在整个request处理过程中被调用最多一次。
该Filter也必须在任何认证机制逻辑发生之前被调用。因为这些认证机制都依赖于SecurityContextHolder所包含的安全上下文对象。
public class SecurityContextPersistenceFilter extends GenericFilterBean {
// 确保该Filter在一个request处理过程中最多被调到用一次的机制:
// 一旦该Fitler被调用过,他会在当前request增加该属性值为true,利用此标记
// 可以避免Filter被调用二次。
static final String FILTER_APPLIED = "__spring_security_scpf_applied";
// 安全上下文存储库
private SecurityContextRepository repo;
private boolean forceEagerSessionCreation = false;
public SecurityContextPersistenceFilter() {
// 缺省使用http session 作为安全上下文对象存储
this(new HttpSessionSecurityContextRepository());
}
public SecurityContextPersistenceFilter(SecurityContextRepository repo) {
this.repo = repo;
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (request.getAttribute(FILTER_APPLIED) != null) {
// ensure that filter is only applied once per request
// 检查调用标志,如果request上已经存在属性FILTER_APPLIED,
// 表明该Filter在该request的处理过程中已经被调用过
chain.doFilter(request, response);
return;
}
final boolean debug = logger.isDebugEnabled();
// 设置该Filter已经被调用的标记
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
if (forceEagerSessionCreation) {
HttpSession session = request.getSession();
if (debug && session.isNew()) {
logger.debug("Eagerly created session: " + session.getId());
}
}
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
response);
// 从安全上下文存储库(缺省是http session)中读取安全上下文对象
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
try {
// 设置安全上下文对象到SecurityContextHolder然后才继续Filter chain的调用
SecurityContextHolder.setContext(contextBeforeChainExecution);
chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
SecurityContext contextAfterChainExecution = SecurityContextHolder
.getContext();
// Crucial removal of SecurityContextHolder contents - do this before anything
// else.
// 当前请求已经被处理完成了,清除SecurityContextHolder并将最新的
// 安全上下文对象保存回安全上下文存储库(缺省是http session)
SecurityContextHolder.clearContext();
repo.saveContext(contextAfterChainExecution, holder.getRequest(),
holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
if (debug) {
logger.debug("SecurityContextHolder now cleared, as request processing completed");
}
}
}
public void setForceEagerSessionCreation(boolean forceEagerSessionCreation) {
this.forceEagerSessionCreation = forceEagerSessionCreation;
}
}