Spring Security 登出的处理流程

1,013 阅读2分钟

Spring Security 的主要功能都是[基于一系列的过滤器实现](Architecture :: Spring Security),而 LogoutFilter 就是其中一个,用于完成用户登出的相关功能。本篇文章主要介绍 LogoutFilter 完成登出流程的细节以及如何完成一些定制化的功能。

LogoutFilter 处理登出请求的主要流程

Spring Security Logout process.png

主要代码:

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
		throws IOException, ServletException {
	if (requiresLogout(request, response)) {
		Authentication auth = this.securityContextHolderStrategy.getContext().getAuthentication();
		if (this.logger.isDebugEnabled()) {
			this.logger.debug(LogMessage.format("Logging out [%s]", auth));
		}
		this.handler.logout(request, response, auth);
		this.logoutSuccessHandler.onLogoutSuccess(request, response, auth);
		return;
	}
	chain.doFilter(request, response);
}

处理流程:

  • 首先判断当前请求是否应该由 LogoutFilter 来处理
  • 然后获取当前 SecurityContext 中的 Authentication
  • 调用 LogoutHandler#logout(request, response, auth) 方法
  • 调用 LogoutSuccessHandler#onLogoutSuccess(request, response, auth) 方法

LogoutHandlerLogoutSuccessHandler 的区别

  • LogoutHandler

    • 用于清除认证信息相关的,要保证成功,不能抛出异常

    • 默认使用的实现类:

      • 1.SecurityContextLogoutHandler 主要用于是清除 session 信息(可配置)和 SecurityContext
      • 2.LogoutSuccessEventPublishingLogoutHandler ,登出成功后发送一个包含登出前认证信息的 LogoutSuccessEvent 事件,用户可以监听此事件来完成诸如 用户登出日志或统计等相关功能
    • 自定义实现类:

      • 自己实现的实现类通过下文的方式配置添加,执行顺序在上述两个默认实现类之前
    • 接口定义:

      /**
       * Indicates a class that is able to participate in logout handling.
       *
       * <p>
       * Called by {@link LogoutFilter}.
       *
       * @author Ben Alex
       */
      public interface LogoutHandler {
      
      	/**
      	 * Causes a logout to be completed. The method must complete successfully.
      	 * @param request the HTTP request
      	 * @param response the HTTP response
      	 * @param authentication the current principal details
      	 */
      	void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication);
      
      }
      
  • LogoutSuccessHandler

    • 用于确定认证信息清除成功后的行为,如 redirect 或 forwarding 到特定页面等

    • 没有配置的情况下默认使用 SimpleUrlLogoutSuccessHandler

    • 接口定义:

      /**
       * Strategy that is called after a successful logout by the {@link LogoutFilter}, to
       * handle redirection or forwarding to the appropriate destination.
       * <p>
       * Note that the interface is almost the same as {@link LogoutHandler} but may raise an
       * exception. <tt>LogoutHandler</tt> implementations expect to be invoked to perform
       * necessary cleanup, so should not throw exceptions.
       *
       * @author Luke Taylor
       * @since 3.0
       */
      public interface LogoutSuccessHandler {
      
      	void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
      			throws IOException, ServletException;
      
      }
      

Logout 行为相关的可配置项(配置 SecurityFilterChain 时的 HttpSecurity中配置)

  • ·LogoutConfigurer· 用于配置 logout 的一些组件:
    • logoutUrl 匹配 LogoutFilter 是否执行 默认 /logout
    • logoutSuccessUrl 登出成功后的重定向地址,默认 /login?logout
    • deleteCookies 要删除的 cookie
    • logoutSuccessHandler 登出成功后的处理器
    • addLogoutHandler SecurityContextLogoutHandler and LogoutSuccessEventPublishingLogoutHandler are added as last LogoutHandler instances by default.
    • clearAuthentication 是否清除 Authentication, 影响 SecurityContextLogoutHandler 的行为
    • invalidateHttpSession 是否 invalide session,影响 SecurityContextLogoutHandler 的行为

应用:如何实现前后端分离项目中前端页面登出后跳转回登出前页面

后端处理:

  • 自定义 LogoutSeccessHandler 实现类,获取登出后要跳转的 redirect_uri 然后重定向到改地址

    @Component
    public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
    
        @Override
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
            String redirect_uri = request.getParameter("redirect_uri");
            request.getSession().invalidate();
            response.sendRedirect(redirect_uri);
        }
    }
    

前端处理:

  • 在请求 /logout 接口时拼接当前页面地址作为 redirect_uri 参数

    // 清除 storage
    // stroge.clear();
    // 跳转登出页
    location.href = `${ssoUrl()}/logout?redirect_uri=` + location.href;
    

参考