`
Dead_knight
  • 浏览: 1201005 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
博客专栏
752c8642-b795-3fe6-946e-a4e845bffdec
Spring Securi...
浏览量:240207
33caa84e-18a6-3036-a82b-6e2106a4de63
clojure专题
浏览量:48915
E17ca077-44df-3816-a3fe-471c43f6e1e5
WebLogic11g
浏览量:236876
社区版块
存档分类
最新评论

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 + "'");
                }
            }
        }
6
0
分享到:
评论

相关推荐

    Spring Security 3 源码分析文档

    通过阅读《Spring Security3.pdf》和《spring security3 源码分析.pdf》这两份文档,你可以对Spring Security 3的内部工作机制有更深入的理解,从而更好地在项目中运用这个强大的安全框架。同时,这也会帮助你在面临...

    spring-security3.1源码

    3. **Filter安全链**:Spring Security 的Web安全功能主要通过一系列过滤器实现,这些过滤器构成了安全链。其中关键的过滤器有`DelegatingFilterProxy`、`ChannelProcessingFilter`、`...

    spring security源码分析.pdf

    ### Spring Security 源码分析知识...以上内容涵盖了 Spring Security 3 的源码分析中几个关键点的具体内容。通过对这些内容的深入学习和理解,可以更好地掌握 Spring Security 的工作原理及其在实际项目中的应用技巧。

    springsecurity源码(鉴权有缺陷)

    然而,正如标题所提及的,Spring Security 的源码可能存在鉴权缺陷。这个话题涉及到多个知识点,包括Spring Security的基本架构、鉴权流程、潜在的安全风险以及如何修复这些问题。 1. **Spring Security 基本架构**...

    Spring_Security3_源码分析

    通过这些源码分析,我们可以了解到Spring Security是如何在背后工作,保护应用程序免受未经授权的访问。理解这些组件的工作原理对于定制和优化安全配置至关重要,同时也有助于开发者解决潜在的安全问题。

    springsecurity

    **源码分析** 深入理解Spring Security的源码有助于定制和优化你的安全解决方案。你可以从以下几个方面入手: - 认证流程:研究`AuthenticationProvider`和`UserDetailsService`的实现。 - 权限控制:查看`...

    spring security简单示例

    6. **源码分析**: Spring Security的源码是开源的,你可以深入研究其内部工作原理,了解每个组件是如何协同工作的。这将帮助你更好地理解和定制这个框架。 7. **工具**: - Spring Security的官方文档和教程是...

    Spring Security tutorial 学习笔记(一)

    在 `SpringSecurityTest01` 这个压缩包文件中,你可能找到了一个示例项目,它可能包含了配置类、控制器、视图和测试用例。通过分析这些代码,你可以更深入地理解如何在实际项目中应用 Spring Security。 总之,...

    SpringSecurity企业及认证全套开发资源.docx

    ### SpringSecurity企业级认证全套开发资源解析 #### 一、Spring Security概述与核心特性 Spring Security 是一个功能强大且高度可定制的身份验证和安全(ACL)框架。它为基于Spring的应用程序提供了声明式方法的...

    springsecurity3.1.3 案例

    每个过滤器都有特定的职责,比如`DelegatingFilterProxy`用于将请求委托给Spring Security的其他组件,`SecurityContextPersistenceFilter`则负责在请求之间保存和恢复安全上下文。 标签提到的"源码"意味着案例可能...

    Spring Security二

    **Spring Security 源码分析** Spring Security 是一个强大的、高度可定制的访问控制和身份认证框架,广泛应用于Java EE应用程序的安全管理。它为开发者提供了丰富的功能,包括身份验证、授权、会话管理以及CSRF...

    spring security 2 学习,是2 不是3

    10. **源码分析**: 深入研究Spring Security 2的源码,有助于理解其工作原理,比如`AuthenticationManager`、`ProviderManager`、`AuthenticationProvider`等核心类的作用。 了解并熟练掌握这些知识点,将使你...

    Spring_security 3.x 登录权限测试模块.以及源码.

    通过学习和分析这些源代码,你可以深入了解Spring Security的工作原理,以及如何将其集成到你的应用程序中以实现安全控制。对于开发人员来说,理解这些核心概念和组件非常重要,因为它们构成了Spring Security强大...

    SpringSecurity研究

    - **SpringSecurity-Study-master**:这个压缩包可能包含一个完整的SpringSecurity学习项目,你可以通过分析源码来加深理解。 6. **进阶话题** - **自定义权限表达式**:除了预定义的权限表达式,如`@...

Global site tag (gtag.js) - Google Analytics