注:Springsecurity版本4.3.x.RELEASEjava
先上一張LogoutFilter的類繼承圖,以下圖1所示,原圖見個人Github。git
圖1 github
LogoutFilter和其它Springsecurity的Filter同樣,都是繼承自GenericFilterBean。web
來看下LogoutFilter的屬性和構造方法,以下List-1所示。當咱們定義瞭如List-2所示的bean時,調用的是List-1中的第二個構造方法。spring
List-1session
public class LogoutFilter extends GenericFilterBean { private RequestMatcher logoutRequestMatcher; private final LogoutHandler handler; private final LogoutSuccessHandler logoutSuccessHandler; /** * Constructor which takes a <tt>LogoutSuccessHandler</tt> instance to determine the * target destination after logging out. The list of <tt>LogoutHandler</tt>s are * intended to perform the actual logout functionality (such as clearing the security * context, invalidating the session, etc.). */ public LogoutFilter(LogoutSuccessHandler logoutSuccessHandler, LogoutHandler... handlers) { this.handler = new CompositeLogoutHandler(handlers); Assert.notNull(logoutSuccessHandler, "logoutSuccessHandler cannot be null"); this.logoutSuccessHandler = logoutSuccessHandler; setFilterProcessesUrl("/logout"); } 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"); }
List-2ui
<bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter"> <constructor-arg value="https://localhost:9443/cas/logout"/> <constructor-arg> <bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/> </constructor-arg> <property name="filterProcessesUrl" value="/logout/cas"/> </bean>
來看下LogoutFilter的doFilter方法,以下List-3所示,this
List-3url
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.onLogoutSuccess(request, response, auth); return; } chain.doFilter(request, response); }
在List-3中:debug
來看下SecurityContextLogoutHandler的logout方法,以下List-4所示,
List-4
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { Assert.notNull(request, "HttpServletRequest required"); if (invalidateHttpSession) { HttpSession session = request.getSession(false); if (session != null) { logger.debug("Invalidating session: " + session.getId()); session.invalidate(); } } if (clearAuthentication) { SecurityContext context = SecurityContextHolder.getContext(); context.setAuthentication(null); } SecurityContextHolder.clearContext(); }
在List-4中:
來看下SimpleUrlLogoutSuccessHandler,如圖2和List-5所示,它直接調用父類AbstractAuthenticationTargetUrlRequestHandler的handle方法,看List-6所示,方法determineTargetUrl決定使用哪一個targetUrl;List-6中handle方法的redirectStrategy.sendRedirect(request, response, targetUrl),調用的DefaultRedirectStrategy的sendRedirect方法,如List-7所示,最終調用的是response的sendRedirect方法。
圖2
List-5
public class SimpleUrlLogoutSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler implements LogoutSuccessHandler { public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { super.handle(request, response, authentication); } }
List-6
protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { String targetUrl = determineTargetUrl(request, response); if (response.isCommitted()) { logger.debug("Response has already been committed. Unable to redirect to " + targetUrl); return; } redirectStrategy.sendRedirect(request, response, targetUrl); } /** * Builds the target URL according to the logic defined in the main class Javadoc. */ protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) { if (isAlwaysUseDefaultTargetUrl()) { return defaultTargetUrl; } // Check for the parameter and use that if available String targetUrl = null; if (targetUrlParameter != null) { targetUrl = request.getParameter(targetUrlParameter); if (StringUtils.hasText(targetUrl)) { logger.debug("Found targetUrlParameter in request: " + targetUrl); return targetUrl; } } if (useReferer && !StringUtils.hasLength(targetUrl)) { targetUrl = request.getHeader("Referer"); logger.debug("Using Referer header: " + targetUrl); } if (!StringUtils.hasText(targetUrl)) { targetUrl = defaultTargetUrl; logger.debug("Using default Url: " + targetUrl); } return targetUrl; }
List-7
public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException { String redirectUrl = calculateRedirectUrl(request.getContextPath(), url); redirectUrl = response.encodeRedirectURL(redirectUrl); if (logger.isDebugEnabled()) { logger.debug("Redirecting to '" + redirectUrl + "'"); } response.sendRedirect(redirectUrl); }