Spring Security Web : AbstractAuthenticationTargetUrlRequestHandler

本文深入探讨了Spring Security中的AbstractAuthenticationTargetUrlRequestHandler类,它是处理认证成功后跳转的核心组件。文章详细分析了其工作原理,包括如何确定跳转URL、何时使用默认URL以及如何利用请求参数和HTTP头部来定制跳转行为。此外,还介绍了两个具体实现:SimpleUrlAuthenticationSuccessHandler和SimpleUrlLogoutSuccessHandler,分别用于登录和登出后的页面跳转。

概述

AbstractAuthenticationTargetUrlRequestHandlerSpring Security Web提供的一个抽象类,它主要实现了这样一种策略 :
认证动作成功时使用一个跳转策略跳转到指定的URL。

该策略实现对应的具体实现方法是 :

    // AbstractAuthenticationTargetUrlRequestHandler 代码片段
	protected void handle(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication) throws IOException, ServletException {
       // 计算跳转目标URL : targetUrl
		String targetUrl = determineTargetUrl(request, response);

		if (response.isCommitted()) {
          // 如果响应对象已经提交,则什么都不做,输出debug日志后直接返回
			logger.debug("Response has already been committed. Unable to redirect to "
					+ targetUrl);
			return;
		}

       // 使用指定的跳转策略跳转到目标url :  targetUrl
		redirectStrategy.sendRedirect(request, response, targetUrl);
	}

Spring Security Web 基于AbstractAuthenticationTargetUrlRequestHandler有两个实现子类 :

  • SimpleUrlAuthenticationSuccessHandler – 登录成功时跳转到指定URL

    参考UsernamePasswordAuthenticationFilterSavedRequestAwareAuthenticationSuccessHandler的使用,
    SavedRequestAwareAuthenticationSuccessHandlerSimpleUrlAuthenticationSuccessHandler的子类。

  • SimpleUrlLogoutSuccessHandler – 退出成功时跳转到指定URL

    参考LogoutFilterLogoutSuccessHandler的使用。

源代码

源代码版本 : Spring Security Web 5.1.4 RELEASE

package org.springframework.security.web.authentication;

// 省略 import 行

public abstract class AbstractAuthenticationTargetUrlRequestHandler {

	protected final Log logger = LogFactory.getLog(this.getClass());
    // 如果通过请求参数指定跳转目标URL,使用此属性指定相应的参数名称,可以设置,
    // 缺省值为 null, 表示不分析请求参数中指定的跳转目标URL
	private String targetUrlParameter = null;
    // 缺省跳转目标 URL,可以设置,
    // 缺省值一般使用 /
	private String defaultTargetUrl = "/";
    // 是否总是使用缺省跳转目标 URL,也就是属性 defaultTargetUrl ,可以设置,
    // 缺省值一般使用 false
	private boolean alwaysUseDefaultTargetUrl = false;
    // 是否使用头部 Referer ,可以设置,
    // 缺省值一般使用 false
	private boolean useReferer = false;
    // 跳转策略,可以设置,, 
    // 缺省使用 DefaultRedirectStrategy
	private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

	protected AbstractAuthenticationTargetUrlRequestHandler() {
	}

	/**
	 * Invokes the configured {@code RedirectStrategy} with the URL returned by the
	 * {@code determineTargetUrl} method.
	 * <p>
	 * The redirect will not be performed if the response has already been committed.
	 */
	protected void handle(HttpServletRequest request, HttpServletResponse response,
			Authentication authentication) throws IOException, ServletException {
        // 计算跳转目标URL : targetUrl     
		String targetUrl = determineTargetUrl(request, response);

		if (response.isCommitted()) {
          // 如果响应对象已经提交,则什么都不做,输出debug日志后直接返回
			logger.debug("Response has already been committed. Unable to redirect to "
					+ targetUrl);
			return;
		}

       // 使用指定的跳转策略跳转到目标url :  targetUrl 
		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) {
       // 如果被设置要求总是使用缺省跳转目标url,则返回缺省跳转目标url : defaultTargetUrl
		if (isAlwaysUseDefaultTargetUrl()) {
			return defaultTargetUrl;
		}

		// Check for the parameter and use that if available
		String targetUrl = null;

       // 如果属性  targetUrlParameter 不为 null, 说明被设置成需要从请求参数中分析跳转目标url
		if (targetUrlParameter != null) {
			targetUrl = request.getParameter(targetUrlParameter);

			if (StringUtils.hasText(targetUrl)) {
              // 如果从请求参数中分析得到跳转目标url,返回该url  
				logger.debug("Found targetUrlParameter in request: " + targetUrl);

				return targetUrl;
			}
		}


       // 如果被设置为要使用请求头部 Referer 模式,并且目标 url 尚未分析得到,
       // 则尝试从请求头部 Referer 获取跳转目标url
		if (useReferer && !StringUtils.hasLength(targetUrl)) {
			targetUrl = request.getHeader("Referer");
			logger.debug("Using Referer header: " + targetUrl);
		}

       // 如果经过以上各种分析逻辑,仍未确定跳转目标url,则跳转目标url使用缺省跳转url,
       // 也就是 defaultTargetUrl
		if (!StringUtils.hasText(targetUrl)) {
			targetUrl = defaultTargetUrl;
			logger.debug("Using default Url: " + targetUrl);
		}

		return targetUrl;
	}

	/**
	 * Supplies the default target Url that will be used if no saved request is found or
	 * the {@code alwaysUseDefaultTargetUrl} property is set to true. If not set, defaults
	 * to {@code /}.
	 *
	 * @return the defaultTargetUrl property
	 */
	protected final String getDefaultTargetUrl() {
		return defaultTargetUrl;
	}

	/**
	 * Supplies the default target Url that will be used if no saved request is found in
	 * the session, or the {@code alwaysUseDefaultTargetUrl} property is set to true. If
	 * not set, defaults to {@code /}. It will be treated as relative to the web-app's
	 * context path, and should include the leading <code>/</code>. Alternatively,
	 * inclusion of a scheme name (such as "http://" or "https://") as the prefix will
	 * denote a fully-qualified URL and this is also supported.
	 *
	 * @param defaultTargetUrl
	 */
	public void setDefaultTargetUrl(String defaultTargetUrl) {
		Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultTargetUrl),
				"defaultTarget must start with '/' or with 'http(s)'");
		this.defaultTargetUrl = defaultTargetUrl;
	}

	/**
	 * If <code>true</code>, will always redirect to the value of {@code defaultTargetUrl}
	 * (defaults to <code>false</code>).
	 */
	public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
		this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
	}

	protected boolean isAlwaysUseDefaultTargetUrl() {
		return alwaysUseDefaultTargetUrl;
	}

	/**
	 * If this property is set, the current request will be checked for this a parameter
	 * with this name and the value used as the target URL if present.
	 *
	 * @param targetUrlParameter the name of the parameter containing the encoded target
	 * URL. Defaults to null.
	 */
	public void setTargetUrlParameter(String targetUrlParameter) {
		if (targetUrlParameter != null) {
			Assert.hasText(targetUrlParameter, "targetUrlParameter cannot be empty");
		}
		this.targetUrlParameter = targetUrlParameter;
	}

	protected String getTargetUrlParameter() {
		return targetUrlParameter;
	}

	/**
	 * Allows overriding of the behaviour when redirecting to a target URL.
	 */
	public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
		this.redirectStrategy = redirectStrategy;
	}

	protected RedirectStrategy getRedirectStrategy() {
		return redirectStrategy;
	}

	/**
	 * If set to {@code true} the {@code Referer} header will be used (if available).
	 * Defaults to {@code false}.
	 */
	public void setUseReferer(boolean useReferer) {
		this.useReferer = useReferer;
	}

}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值