Java学习笔记spring security程序员

Spring Security源码分析之LogoutFilter

2018-05-04  本文已影响68人  紫霞等了至尊宝五百年

LogoutFilter过滤器对应的类路径为
org.springframework.security.web.authentication.logout.LogoutFilter
通过这个类的源码可以看出,这个类有两个构造函数



这两个构造函数的参数,就是之前解析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");  
        }  

        this.handler.logout(request, response, auth);

        //退出成功后,进行redirect操作  
        logoutSuccessHandler.onLogoutSuccess(request, response, auth);  
  
        return;  
    }  
  
    chain.doFilter(request, response);  
}  

上一个过滤器 SecurityContextPersistenceFilter 不是只产生了一个空的SecurityContext嘛?就是一个没有认证信息的 SecurityContext 实例


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

继续看LogoutHandler是如何处理退出任务的



这里的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



完成两个任务

再来看remember me的handler
1.配置了持久化属性时的handler:PersistentTokenBasedRememberMeServices



也完成两个任务

如果定义了token-repository-ref属性,则通过依赖的持久化bean清除
如果定义了data-source-ref属性,直接通过
JdbcTokenRepositoryImpl清除数据,也就是执行delete操作

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

上一篇 下一篇

猜你喜欢

热点阅读