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

Spring Security3源码分析-LogoutFilter分析

 
阅读更多
LogoutFilter过滤器对应的类路径为
org.springframework.security.web.authentication.logout.LogoutFilter
通过这个类的源码可以看出,这个类有两个构造函数
    public LogoutFilter(LogoutSuccessHandler logoutSuccessHandler, LogoutHandler... handlers) {
        Assert.notEmpty(handlers, "LogoutHandlers are required");
        this.handlers = Arrays.asList(handlers);
        Assert.notNull(logoutSuccessHandler, "logoutSuccessHandler cannot be null");
        this.logoutSuccessHandler = logoutSuccessHandler;
    }

    public LogoutFilter(String logoutSuccessUrl, LogoutHandler... handlers) {
        Assert.notEmpty(handlers, "LogoutHandlers are required");
        this.handlers = Arrays.asList(handlers);
        Assert.isTrue(!StringUtils.hasLength(logoutSuccessUrl) ||
                UrlUtils.isValidRedirectUrl(logoutSuccessUrl), logoutSuccessUrl + " isn't a valid redirect URL");
        SimpleUrlLogoutSuccessHandler urlLogoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();
        if (StringUtils.hasText(logoutSuccessUrl)) {
            urlLogoutSuccessHandler.setDefaultTargetUrl(logoutSuccessUrl);
        }
        logoutSuccessHandler = urlLogoutSuccessHandler;
    }

这两个构造函数的参数,是从哪里传递的呢?没错,就是之前解析http标签通过创建LogoutFilter过滤器的bean定义时通过构造参数注入进来的。下面的部分源码为LogoutFilter的bean定义
public BeanDefinition parse(Element element, ParserContext pc) {
        String logoutUrl = null;
        String successHandlerRef = null;
        String logoutSuccessUrl = null;
        String invalidateSession = null;

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(LogoutFilter.class);

        if (element != null) {
            //分别解析logout标签的属性
            Object source = pc.extractSource(element);
            builder.getRawBeanDefinition().setSource(source);
            logoutUrl = element.getAttribute(ATT_LOGOUT_URL);
            successHandlerRef = element.getAttribute(ATT_LOGOUT_HANDLER);
            WebConfigUtils.validateHttpRedirect(logoutUrl, pc, source);
            logoutSuccessUrl = element.getAttribute(ATT_LOGOUT_SUCCESS_URL);
            WebConfigUtils.validateHttpRedirect(logoutSuccessUrl, pc, source);
            invalidateSession = element.getAttribute(ATT_INVALIDATE_SESSION);
        }

        if (!StringUtils.hasText(logoutUrl)) {
            logoutUrl = DEF_LOGOUT_URL;
        }
        //向LogoutFilter中注入属性值filterProcessesUrl
        builder.addPropertyValue("filterProcessesUrl", logoutUrl);

        if (StringUtils.hasText(successHandlerRef)) {
            if (StringUtils.hasText(logoutSuccessUrl)) {
                pc.getReaderContext().error("Use " + ATT_LOGOUT_URL + " or " + ATT_LOGOUT_HANDLER + ", but not both",
                        pc.extractSource(element));
            }
            //如果successHandlerRef不为空,就通过构造函数注入到LogoutFilter中
            builder.addConstructorArgReference(successHandlerRef);
        } else {
            // Use the logout URL if no handler set
            if (!StringUtils.hasText(logoutSuccessUrl)) {
                //如果logout-success-url没有定义,则采用默认的/
                logoutSuccessUrl = DEF_LOGOUT_SUCCESS_URL;
            }
            //通过构造函数注入logoutSuccessUrl值
            builder.addConstructorArgValue(logoutSuccessUrl);
        }

        if (!StringUtils.hasText(invalidateSession)) {
            invalidateSession = DEF_INVALIDATE_SESSION;
        }
        //默认Logout的Handler是SecurityContextLogoutHandler
        ManagedList handlers = new ManagedList();
        SecurityContextLogoutHandler sclh = new SecurityContextLogoutHandler();
        if ("true".equals(invalidateSession)) {
            sclh.setInvalidateHttpSession(true);
        } else {
            sclh.setInvalidateHttpSession(false);
        }
        handlers.add(sclh);
        //如果有remember me服务,需要添加remember的handler
        if (rememberMeServices != null) {
            handlers.add(new RuntimeBeanReference(rememberMeServices));
        }
        //继续将handlers通过构造参数注入到LogoutFilter的bean中
        builder.addConstructorArgValue(handlers);

        return builder.getBeanDefinition();
    }

此时应该能知道,在LogoutFilter的bean实例化时,两个类变量logoutSuccessUrl、List<LogoutHandler> handlers已经通过构造函数注入到LogoutFilter的实例中来了。
接下来,继续看doFilter部分的源码
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        //判断是否需要退出,主要通过请求的url是否是filterProcessesUrl值来识别
        if (requiresLogout(request, response)) {
            //通过SecurityContext实例获取认证信息
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();

            if (logger.isDebugEnabled()) {
                logger.debug("Logging out user '" + auth + "' and transferring to logout destination");
            }
            //循环LogoutHandler处理退出任务
            for (LogoutHandler handler : handlers) {
                handler.logout(request, response, auth);
            }
            //退出成功后,进行redirect操作
            logoutSuccessHandler.onLogoutSuccess(request, response, auth);

            return;
        }

        chain.doFilter(request, response);
    }

这时,可能会产生疑问。上一个过滤器SecurityContextPersistenceFilter不是只产生了一个空的SecurityContext么?就是一个没有认证信息的SecurityContext实例
Authentication auth = SecurityContextHolder.getContext().getAuthentication();

这个返回的不是null么?产生这个疑问,肯定是被SecurityContextPersistenceFilter过滤器分析时误导的。实际上,每个过滤器只处理自己负责的事情,LogoutFilter只负责拦截j_spring_security_logout这个url(如果没有配置logout的url),其他的url全部跳过。其实退出功能肯定是登录到应用之后才会使用到的,登录对应的Filter肯定会把认证信息添加到SecurityContext中去的,后面再分析。

继续看LogoutHandler是如何处理退出任务的
            for (LogoutHandler handler : handlers) {
                handler.logout(request, response, auth);
            }

这里的handler至少有一个SecurityContextLogoutHandler,
如果有remember me服务,就还有一个Handler。remember me的handler有两种,
如果配置了持久化信息,如(token-repository-ref、data-source-ref属性)这种的handler为:org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices
如果没有配置,那么handler就是:org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices

先来看SecurityContextLogoutHandler
    //完成两个任务1.让session失效;2.清除SecurityContext实例
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
        Assert.notNull(request, "HttpServletRequest required");
        if (invalidateHttpSession) {
            HttpSession session = request.getSession(false);
            if (session != null) {
                session.invalidate();
            }
        }

        SecurityContextHolder.clearContext();
    }


再来看remember me的handler
1.配置了持久化属性时的handler:PersistentTokenBasedRememberMeServices
    //也完成两个任务1.清除cookie;2.从持久化中清除remember me数据
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
        super.logout(request, response, authentication);

        if (authentication != null) {
            //如果定义了token-repository-ref属性,则通过依赖的持久化bean清除
              //如果定义了data-source-ref属性,直接通过
              //JdbcTokenRepositoryImpl清除数据,也就是执行delete操作
            tokenRepository.removeUserTokens(authentication.getName());
        }
    }

2.未配置持久化属性的handler:TokenBasedRememberMeServices
这个handler没有覆盖父类的logout方法,所以直接调用父类的logout方法,仅仅清除cookie

退出成功后执行onLogoutSuccess操作,完成redirect
logoutSuccessHandler.onLogoutSuccess(request, response, auth);

这个语句是直接redirect到logout标签中的logout-success-url属性定义的url

至此,整个logoutFilter任务已经完成了,总结一下,主要任务为
1.从SecurityContext中获取Authentication,然后调用每个handler处理logout
2.退出成功后跳转到指定的url
分享到:
评论
1 楼 fcs_our2010 2016-10-30  
挺详细的。。

相关推荐

    spring security 3.1.3 源码含sample源码

    综上,Spring Security 3.1.3源码的分析和学习,可以帮助开发者深入理解Web安全原理,掌握如何利用这个强大的框架来保护应用程序免受攻击。通过对源码的研究,可以更清晰地了解其内部工作方式,从而更好地进行定制化...

    spring security源码分析.pdf

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

    Spring_Security3_源码分析

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

    springsecurity

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

    spring security简单示例

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

    spring security source code

    《深入剖析Spring Security源码与Spring Boot集成》 在当今的Web开发中,安全问题始终是不可忽视的重要环节。Spring Security作为Spring生态系统中的一个强大安全框架,为开发者提供了丰富的功能来保护应用程序。...

    我的SpringSecurity实践

    这篇博文的作者通过实际操作分享了他在使用SpringSecurity时的经验,虽然描述中并未给出具体细节,但我们可以从标题和标签中推测,本文可能涉及了SpringSecurity的源码分析和工具的使用。 SpringSecurity的核心概念...

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

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

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

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

    acegi源码解读.txtacegi源码解读.txtacegi源码解读.txt

    而acegi,作为Spring Security早期的名字,其源码分析对于理解Spring Security的工作原理具有不可替代的价值。本文将深入解析acegi(即Spring Security)的核心组件及其实现机制。 #### acegi核心组件与工作流程 #...

Global site tag (gtag.js) - Google Analytics