Spring Security3源码分析(5)-SecurityContextPersistenceFilter分析

通过观察Filter的名字,就能大概猜出来这个过滤器的作用,是的,持久化SecurityContext实例。这个过滤器位置是; 

org.springframework.security.web.context.SecurityContextPersistenceFilter 

废话不说,看源码 

Java代码  

  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)  
  2.         throws IOException, ServletException {  
  3.     HttpServletRequest request = (HttpServletRequest) req;  
  4.     HttpServletResponse response = (HttpServletResponse) res;  
  5.     if (request.getAttribute(FILTER_APPLIED) != null) {  
  6.         // ensure that filter is only applied once per request  
  7.         chain.doFilter(request, response);  
  8.         return;  
  9.     }  
  10.   
  11.     final boolean debug = logger.isDebugEnabled();  
  12.   
  13.     request.setAttribute(FILTER_APPLIED, Boolean.TRUE);  
  14.   
  15.     if (forceEagerSessionCreation) {  
  16.         HttpSession session = request.getSession();  
  17.   
  18.         if (debug && session.isNew()) {  
  19.             logger.debug(“Eagerly created session: “ + session.getId());  
  20.         }  
  21.     }  
  22.     //将request、response对象交给HttpRequestResponseHolder维持  
  23.     HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);  
  24.     //通过SecurityContextRepository接口的实现类装载SecurityContext实例  
  25.     //HttpSessionSecurityContextRepository将产生SecurityContext实例的任务交给SecurityContextHolder.createEmptyContext()完成  
  26.     //SecurityContextHolder再根据策略模式的不同,  
  27.     //把任务再交给相应策略类完成SecurityContext的创建  
  28.     //如果没有配置策略名称,则默认为  
  29.     //ThreadLocalSecurityContextHolderStrategy,  
  30.     //该类直接通过new SecurityContextImpl()创建实例  
  31.     SecurityContext contextBeforeChainExecution = repo.loadContext(holder);  
  32.   
  33.     try {  
  34.         //将产生的SecurityContext再通过SecurityContextHolder->  
  35.       //ThreadLocalSecurityContextHolderStrategy设置到ThreadLocal中  
  36.         SecurityContextHolder.setContext(contextBeforeChainExecution);  
  37.         //继续把请求流向下一个过滤器执行  
  38.         chain.doFilter(holder.getRequest(), holder.getResponse());  
  39.   
  40.     } finally {  
  41.         //先从SecurityContextHolder获取SecurityContext实例  
  42.         SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();  
  43.         // Crucial removal of SecurityContextHolder contents – do this before anything else.  
  44.         //再把SecurityContext实例从SecurityContextHolder中清空  
  45.         SecurityContextHolder.clearContext();  
  46.         //将SecurityContext实例持久化到session中  
  47.         repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());  
  48.         request.removeAttribute(FILTER_APPLIED);  
  49.         if (debug) {  
  50.             logger.debug(“SecurityContextHolder now cleared, as request processing completed”);  
  51.         }  
  52.     }  
  53. }  

通过源码中的注释,应该可以看出来,这个Filter的作用主要是创建一个空的SecurityContext(如果session中没有SecurityContext实例),然后持久化到session中。 

接下来看看repo.loadContext(holder);代码: 

Java代码  

  1. public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {  
  2.     HttpServletRequest request = requestResponseHolder.getRequest();  
  3.     HttpServletResponse response = requestResponseHolder.getResponse();  
  4.     HttpSession httpSession = request.getSession(false);  
  5.     //从session中获取SecurityContext  
  6.      SecurityContext context = readSecurityContextFromSession(httpSession);  
  7.     //如果获取不到SecurityContext,新建一个空的SecurityContext实例  
  8.     if (context == null) {  
  9.         if (logger.isDebugEnabled()) {  
  10.             logger.debug(“No SecurityContext was available from the HttpSession: “ + httpSession +“. “ +  
  11.                     “A new one will be created.”);  
  12.         }  
  13.         context = generateNewContext();  
  14.   
  15.     }  
  16.     //这里需要注意一下,response装饰器类重新包装了response  
  17.     requestResponseHolder.setResponse(new SaveToSessionResponseWrapper(response, request,  
  18.             httpSession != null, context.hashCode()));  
  19.   
  20.     return context;  
  21. }  

进一步分析generateNewContext方法 

Java代码  

  1. SecurityContext generateNewContext() {  
  2.     SecurityContext context = null;  
  3.     //创建SecurityContext实例并返回  
  4.     if (securityContextClass == null) {  
  5.         context = SecurityContextHolder.createEmptyContext();  
  6.   
  7.         return context;  
  8.     }  
  9.   
  10.     try {  
  11.         context = securityContextClass.newInstance();  
  12.     } catch (Exception e) {  
  13.         ReflectionUtils.handleReflectionException(e);  
  14.     }  
  15.     return context;  
  16. }  

实际上,SecurityContextHolder类也是把创建SecurityContext任务交给具体的SecurityContextHolderStrategy实现类处理,SecurityContextHolder类有一个静态初始化过程 

Java代码  

  1. static {  
  2.     initialize();  
  3. }  
  4.   
  5.   
  6. private static void initialize() {  
  7.     if ((strategyName == null) || “”.equals(strategyName)) {  
  8.         // Set default  
  9.         strategyName = MODE_THREADLOCAL;  
  10.     }  
  11.     //默认的SecurityContextHolderStrategy实现类为  
  12.      //ThreadLocalSecurityContextHolderStrategy  
  13.     if (strategyName.equals(MODE_THREADLOCAL)) {  
  14.         strategy = new ThreadLocalSecurityContextHolderStrategy();  
  15.     } else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {  
  16.         strategy = new InheritableThreadLocalSecurityContextHolderStrategy();  
  17.     } else if (strategyName.equals(MODE_GLOBAL)) {  
  18.         strategy = new GlobalSecurityContextHolderStrategy();  
  19.     } else {  
  20.         // Try to load a custom strategy  
  21.         try {  
  22.             Class<?> clazz = Class.forName(strategyName);  
  23.             Constructor<?> customStrategy = clazz.getConstructor();  
  24.             strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();  
  25.         } catch (Exception ex) {  
  26.             ReflectionUtils.handleReflectionException(ex);  
  27.         }  
  28.     }  
  29.   
  30.     initializeCount++;  
  31. }  

现在来看ThreadLocalSecurityContextHolderStrategy源码 

Java代码  

  1. final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {  
  2.     //~ Static fields/initializers =====================================================================================  
  3.   
  4.     private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<SecurityContext>();  
  5.   
  6.     //~ Methods ========================================================================================================  
  7.   
  8.     public void clearContext() {  
  9.         contextHolder.set(null);  
  10.     }  
  11.   
  12.     public SecurityContext getContext() {  
  13.         SecurityContext ctx = contextHolder.get();  
  14.   
  15.         if (ctx == null) {  
  16.             ctx = createEmptyContext();  
  17.             contextHolder.set(ctx);  
  18.         }  
  19.   
  20.         return ctx;  
  21.     }  
  22.   
  23.     public void setContext(SecurityContext context) {  
  24.         Assert.notNull(context, “Only non-null SecurityContext instances are permitted”);  
  25.         contextHolder.set(context);  
  26.     }  
  27.     //直接new一个SecurityContextImpl对象,  
  28.      //SecurityContextImpl类实现SecurityContext接口  
  29.     public SecurityContext createEmptyContext() {  
  30.         return new SecurityContextImpl();  
  31.     }  
  32. }  

分析到这里,整个过程也清楚了。不过在filter原路返回时,还需要保存这个SecurityContext实例到session中,并且通过SecurityContextHolder将ThreadLocalSecurityContextHolderStrategy中ThreadLocal维持的SecurityContext实例清空。 

Java代码  

  1. //将SecurityContext实例持久化到session中  
  2.   repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());  

Java代码  

  1. public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {  
  2.     //由于之前response装饰器类SaveToSessionResponseWrapper  
  3.     //重新装饰了response  
  4.     SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = (SaveContextOnUpdateOrErrorResponseWrapper)response;  
  5.     // saveContext() might already be called by the response wrapper  
  6.     // if something in the chain called sendError() or sendRedirect(). This ensures we only call it  
  7.     // once per request.  
  8.     if (!responseWrapper.isContextSaved() ) {  
  9.         //SaveToSessionResponseWrapper保存SecurityContext实例  
  10.         responseWrapper.saveContext(context);  
  11.     }  
  12. }  

SaveToSessionResponseWrapper的saveContext方法源码: 

Java代码  

  1. protected void saveContext(SecurityContext context) {  
  2.     // See SEC-776  
  3.     if (authenticationTrustResolver.isAnonymous(context.getAuthentication())) {  
  4.         if (logger.isDebugEnabled()) {  
  5.             logger.debug(“SecurityContext contents are anonymous – context will not be stored in HttpSession. “);  
  6.         }  
  7.         return;  
  8.     }  
  9.   
  10.     HttpSession httpSession = request.getSession(false);  
  11.   
  12.     if (httpSession == null) {  
  13.         httpSession = createNewSessionIfAllowed(context);  
  14.     }  
  15.   
  16.     // If HttpSession exists, store current SecurityContextHolder contents but only if  
  17.     // the SecurityContext has actually changed (see JIRA SEC-37)  
  18.     if (httpSession != null && context.hashCode() != contextHashBeforeChainExecution) {  
  19.         //保存SecurityContext到session中  
  20.         httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);  
  21.   
  22.         if (logger.isDebugEnabled()) {  
  23.             logger.debug(“SecurityContext stored to HttpSession: ‘” + context + “‘”);  
  24.         }  
  25.     }  
  26. }  
    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/benjamin_whx/article/details/39204611
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞