zoukankan      html  css  js  c++  java
  • 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 + "'");
                    }
                }
            }
    

    http://www.linuxso.com/architecture/28527.html

  • 相关阅读:
    Python 文件去重(转载)
    Mongodb 3.6.5 Compass可视化工具的使用
    mongodb启动错误(Failed to set up listener: SocketException: Cannot assign requested address)
    MongDB 配置参数设置
    MongDB 配置参数查看
    Python 可视化篇:流式数据监控(转载)
    CentOS7 5 ansible playbook 示例
    CentOS7 3 模块介绍 ansible
    CentOS7 2 配置 ansible
    Linux下离线安装python项目的依赖包 (转载)
  • 原文地址:https://www.cnblogs.com/PatrickLee/p/2614544.html
Copyright © 2011-2022 走看看