Spring Security3源码分析-SecurityContextPersistenceFilter分析
通过观察Filter的名字,就能大概猜出来这个过滤器的作用,是的,持久化SecurityContext实例。这个过滤器位置是;
org.springframework.security.web.context.SecurityContextPersistenceFilter
废话不说,看源码
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 chain.doFilter(request, response); return; } final boolean debug = logger.isDebugEnabled(); request.setAttribute(FILTER_APPLIED, Boolean.TRUE); if (forceEagerSessionCreation) { HttpSession session = request.getSession(); if (debug && session.isNew()) { logger.debug("Eagerly created session: " + session.getId()); } } //将request、response对象交给HttpRequestResponseHolder维持 HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response); //通过SecurityContextRepository接口的实现类装载SecurityContext实例 //HttpSessionSecurityContextRepository将产生SecurityContext实例的任务交给SecurityContextHolder.createEmptyContext()完成 //SecurityContextHolder再根据策略模式的不同, //把任务再交给相应策略类完成SecurityContext的创建 //如果没有配置策略名称,则默认为 //ThreadLocalSecurityContextHolderStrategy, //该类直接通过new SecurityContextImpl()创建实例 SecurityContext contextBeforeChainExecution = repo.loadContext(holder); try { //将产生的SecurityContext再通过SecurityContextHolder-> //ThreadLocalSecurityContextHolderStrategy设置到ThreadLocal中 SecurityContextHolder.setContext(contextBeforeChainExecution); //继续把请求流向下一个过滤器执行 chain.doFilter(holder.getRequest(), holder.getResponse()); } finally { //先从SecurityContextHolder获取SecurityContext实例 SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext(); // Crucial removal of SecurityContextHolder contents - do this before anything else. //再把SecurityContext实例从SecurityContextHolder中清空 SecurityContextHolder.clearContext(); //将SecurityContext实例持久化到session中 repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse()); request.removeAttribute(FILTER_APPLIED); if (debug) { logger.debug("SecurityContextHolder now cleared, as request processing completed"); } } }
通过源码中的注释,应该可以看出来,这个Filter的作用主要是创建一个空的SecurityContext(如果session中没有SecurityContext实例),然后持久化到session中。
接下来看看repo.loadContext(holder);代码:
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) { HttpServletRequest request = requestResponseHolder.getRequest(); HttpServletResponse response = requestResponseHolder.getResponse(); HttpSession httpSession = request.getSession(false); //从session中获取SecurityContext SecurityContext context = readSecurityContextFromSession(httpSession); //如果获取不到SecurityContext,新建一个空的SecurityContext实例 if (context == null) { if (logger.isDebugEnabled()) { logger.debug("No SecurityContext was available from the HttpSession: " + httpSession +". " + "A new one will be created."); } context = generateNewContext(); } //这里需要注意一下,response装饰器类重新包装了response requestResponseHolder.setResponse(new SaveToSessionResponseWrapper(response, request, httpSession != null, context.hashCode())); return context; }
进一步分析generateNewContext方法
SecurityContext generateNewContext() { SecurityContext context = null; //创建SecurityContext实例并返回 if (securityContextClass == null) { context = SecurityContextHolder.createEmptyContext(); return context; } try { context = securityContextClass.newInstance(); } catch (Exception e) { ReflectionUtils.handleReflectionException(e); } return context; }
实际上,SecurityContextHolder类也是把创建SecurityContext任务交给具体的SecurityContextHolderStrategy实现类处理,SecurityContextHolder类有一个静态初始化过程
static { initialize(); } ………… private static void initialize() { if ((strategyName == null) || "".equals(strategyName)) { // Set default strategyName = MODE_THREADLOCAL; } //默认的SecurityContextHolderStrategy实现类为 //ThreadLocalSecurityContextHolderStrategy if (strategyName.equals(MODE_THREADLOCAL)) { strategy = new ThreadLocalSecurityContextHolderStrategy(); } else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) { strategy = new InheritableThreadLocalSecurityContextHolderStrategy(); } else if (strategyName.equals(MODE_GLOBAL)) { strategy = new GlobalSecurityContextHolderStrategy(); } else { // Try to load a custom strategy try { Class<?> clazz = Class.forName(strategyName); Constructor<?> customStrategy = clazz.getConstructor(); strategy = (SecurityContextHolderStrategy) customStrategy.newInstance(); } catch (Exception ex) { ReflectionUtils.handleReflectionException(ex); } } initializeCount++; }
现在来看ThreadLocalSecurityContextHolderStrategy源码
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy { //~ Static fields/initializers ===================================================================================== private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<SecurityContext>(); //~ Methods ======================================================================================================== public void clearContext() { contextHolder.set(null); } public SecurityContext getContext() { SecurityContext ctx = contextHolder.get(); if (ctx == null) { ctx = createEmptyContext(); contextHolder.set(ctx); } return ctx; } public void setContext(SecurityContext context) { Assert.notNull(context, "Only non-null SecurityContext instances are permitted"); contextHolder.set(context); } //直接new一个SecurityContextImpl对象, //SecurityContextImpl类实现SecurityContext接口 public SecurityContext createEmptyContext() { return new SecurityContextImpl(); } }
分析到这里,整个过程也清楚了。不过在filter原路返回时,还需要保存这个SecurityContext实例到session中,并且通过SecurityContextHolder将ThreadLocalSecurityContextHolderStrategy中ThreadLocal维持的SecurityContext实例清空。
//将SecurityContext实例持久化到session中 repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) { //由于之前response装饰器类SaveToSessionResponseWrapper //重新装饰了response SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = (SaveContextOnUpdateOrErrorResponseWrapper)response; // saveContext() might already be called by the response wrapper // if something in the chain called sendError() or sendRedirect(). This ensures we only call it // once per request. if (!responseWrapper.isContextSaved() ) { //SaveToSessionResponseWrapper保存SecurityContext实例 responseWrapper.saveContext(context); } }
SaveToSessionResponseWrapper的saveContext方法源码:
protected void saveContext(SecurityContext context) { // See SEC-776 if (authenticationTrustResolver.isAnonymous(context.getAuthentication())) { if (logger.isDebugEnabled()) { logger.debug("SecurityContext contents are anonymous - context will not be stored in HttpSession. "); } return; } HttpSession httpSession = request.getSession(false); if (httpSession == null) { httpSession = createNewSessionIfAllowed(context); } // If HttpSession exists, store current SecurityContextHolder contents but only if // the SecurityContext has actually changed (see JIRA SEC-37) if (httpSession != null && context.hashCode() != contextHashBeforeChainExecution) { //保存SecurityContext到session中 httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, context); if (logger.isDebugEnabled()) { logger.debug("SecurityContext stored to HttpSession: '" + context + "'"); } } }