Springsecurity之LogoutFilter

    注:Springsecurity版本4.3.x.RELEASEjava

    先上一張LogoutFilter的類繼承圖,以下圖1所示,原圖見個人Githubgit

                                                               圖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

  1. requiresLogout方法判斷requst中的url是不是/logout/cas,見List-2中的屬性filterProcessesUrl
  2. 若是知足步驟1的要求,那麼調用LogoutHandler的logout方法,會將Session失效,此外將SecurityContextHolder清空
  3. 若是知足步驟1的要求,那麼調用LogoutSuccessHandler的onLogoutSuccess方法,設置HttpServletResponse的重定向
  4. 若是知足步驟1的要求,那麼不會調用FilterChain了

    來看下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中:

  1. 會將HttpSession失效
  2. 清空SecurityContextHolder的context

    來看下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);
}
相關文章
相關標籤/搜索