Spring Security Web 5.1.2 源码解析 -- LogoutFilter

概述

在进行安全配置时,不管是明确指定还是使用缺省配置,最终安全配置中都会包含以下退出登录配置信息:

  • 怎样的请求是一个退出登录请求
    • 这里包含两部分信息: url, http method
  • 成功退出登录过程需要做哪些事情
    • 也就是各种配置的LogoutHandler
    • 核心LogoutHandler:SecurityContextLogoutHandler–销毁sessionSecurityContextHolder内容
  • 成功退出登录后跳转到哪里
    • 也就是配置中的 logoutSuccessUrl

基于以上配置信息,LogoutFilter被设计用于检测用户退出登录请求,执行相应的处理工作以及退出登录后的页面跳转。

源代码解析

package org.springframework.security.web.authentication.logout;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.GenericFilterBean;

public class LogoutFilter extends GenericFilterBean {

    // ~ Instance fields
    // ================================================================================================

    private RequestMatcher logoutRequestMatcher;

    private final LogoutHandler handler;
    private final LogoutSuccessHandler logoutSuccessHandler;

    // ~ Constructors
    // ===================================================================================================

    /** * Constructor which takes a LogoutSuccessHandler instance to determine the * target destination after logging out. The list of LogoutHandlers are * intended to perform the actual logout functionality (such as clearing the security * context, invalidating the session, etc.). * 缺省情况下,这里的LogoutSuccessHandler是一个SimpleUrlLogoutSuccessHandler实例, * 在退出登录成功时跳转到/。 * * 安全配置信息中还会包含对cookie,remember me 等安全机制的配置,这些机制中在用户成功退出 * 登录时也会执行一些相应的清场工作,这些工作就是通过参数handlers传递进来的。这些handlers * 中最核心的一个就是SecurityContextLogoutHandler,它会销毁session和针对当前请求的 * SecurityContextHolder中的安全上下文对象,这是真正意义上的退出登录。 */
    public LogoutFilter(LogoutSuccessHandler logoutSuccessHandler,
            LogoutHandler... handlers) {
        this.handler = new CompositeLogoutHandler(handlers);
        Assert.notNull(logoutSuccessHandler, "logoutSuccessHandler cannot be null");
        this.logoutSuccessHandler = logoutSuccessHandler;
        // 定义一个缺省的用户退出登录请求匹配器:
        // 只要用户请求/logout而无论http method是什么,都认为是要退出登录了,
        // 该缺省值通常会被安全配置覆盖,请留意
        setFilterProcessesUrl("/logout");
    }

    // 另外一个构造函数,如果没有指定logoutSuccessHandler,而是只指定了logoutSuccessUrl,
    // 该方法会根据logoutSuccessUrl构造一个logoutSuccessHandler:SimpleUrlLogoutSuccessHandler
    public LogoutFilter(String logoutSuccessUrl, LogoutHandler... handlers) {
        this.handler = new CompositeLogoutHandler(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;
        setFilterProcessesUrl("/logout");
    }

    // ~ Methods
    // ========================================================================================================

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        
        if (requiresLogout(request, response)) {
        // 检测到用户请求了退出当前登录,现在执行退出登录逻辑
            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);

            // 缺省情况下,这里的LogoutSuccessHandler是一个SimpleUrlLogoutSuccessHandler实例,
            // 在退出登录成功时跳转到/。
            // 上面已经成功退出了用户登录,现在跳转到相应的页面
            logoutSuccessHandler.onLogoutSuccess(request, response, auth);
            
            // 注意,这里完成了用户退出登录动作和页面跳转,所以当前请求的处理任务已经结束,
            // 也就是说不用再继续filter chain的执行了,直接函数返回即可。
            return;
        }

        // 不是用户请求退出登录的情况,继续执行 filter chain 。
        chain.doFilter(request, response);
    }

    /** * Allow subclasses to modify when a logout should take place. * 根据当前请求和安全配置检测是否用户在请求退出登录,如果是用户在请求退出登录的情况返回true, * 否则返回false * @param request the request * @param response the response * * @return true if logout should occur, false otherwise */
    protected boolean requiresLogout(HttpServletRequest request,
            HttpServletResponse response) {
        // logoutRequestMatcher 是配置时明确指定的,或者是根据其他配置计算出来的
        return logoutRequestMatcher.matches(request);
    }

    // 配置阶段会将用户明确指定的logoutRequestMatcher或者根据其他配置计算出来的logoutRequestMatcher
    // 通过该方法设置到当前Filter对象
    public void setLogoutRequestMatcher(RequestMatcher logoutRequestMatcher) {
        Assert.notNull(logoutRequestMatcher, "logoutRequestMatcher cannot be null");
        this.logoutRequestMatcher = logoutRequestMatcher;
    }

    // 调用该方法则会将当前Filter的logoutRequestMatcher设置为一个根据filterProcessesUrl计算出来的
    //AntPathRequestMatcher,该matcher会仅根据请求url进行匹配,而不管http method是什么
    //
    // 在该Filter的构造函数中就调用了该方法setFilterProcessesUrl("/logout"),从而构建了一个缺省的
    // AntPathRequestMatcher,表示只要用户访问 url /logout,不管http method是什么,都认为用户想要
    // 退出登录。但实际上,该初始值都会被配置过程中根据用户配置信息计算出的AntPathRequestMatcher
    // 调用上面的setLogoutRequestMatcher(logoutRequestMatcher)覆盖该matcher 
    public void setFilterProcessesUrl(String filterProcessesUrl) {
        this.logoutRequestMatcher = new AntPathRequestMatcher(filterProcessesUrl);
    }
}

参考文章

    原文作者:Spring Cloud
    原文地址: https://blog.csdn.net/andy_zhang2007/article/details/84727097
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞