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
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞