SpringSecurity - 基础概念之 RequestMatcher

概述

RequestMatcher:请求匹配器,定义了 match 方法,匹配请求 HttpServletRequest 是否符合定义的匹配规则,具体匹配逻辑由实现类来实现,返回 true 表示匹配成功。同时定义了一个默认方法和一个内部类,除了提供匹配的结果,还提供一个 Map<String, String> 来存储变量。

public interface RequestMatcher {

	boolean matches(HttpServletRequest request);

	default MatchResult matcher(HttpServletRequest request) {
		boolean match = matches(request);
		return new MatchResult(match, Collections.emptyMap());
	}

	class MatchResult {

		private final boolean match;

		private final Map<String, String> variables;

		...
	}
}

RequestMatcher 有众多的实现类:

在这里插入图片描述

在这里插入图片描述

常用实现类

AnyRequestMatcher

可以看到 matches 直接返回 true,表示匹配所有请求

public final class AnyRequestMatcher implements RequestMatcher {

	public static final RequestMatcher INSTANCE = new AnyRequestMatcher();

	private AnyRequestMatcher() {
	}

	@Override
	public boolean matches(HttpServletRequest request) {
		return true;
	}

	...
}

AntPathRequestMatcher

关于 Ant 匹配模式可以查看 Sping - Ant-style pattern 这篇文章

public final class AntPathRequestMatcher implements RequestMatcher, RequestVariablesExtractor {

	private static final String MATCH_ALL = "/**";

	private final Matcher matcher;

	private final String pattern;

	private final HttpMethod httpMethod;

	private final boolean caseSensitive;

	private final UrlPathHelper urlPathHelper;

	// 利用面向对象的多态特性定义了几个构造器

	/**
	 * Creates a matcher with the supplied pattern which will match the specified Http
	 * method
	 * @param pattern the ant pattern to use for matching
	 * @param httpMethod the HTTP method. The {@code matches} method will return false if
	 * the incoming request doesn't have the same method.
	 * @param caseSensitive true if the matcher should consider case, else false
	 * @param urlPathHelper if non-null, will be used for extracting the path from the
	 * HttpServletRequest
	 */
	public AntPathRequestMatcher(String pattern, String httpMethod, boolean caseSensitive,
			UrlPathHelper urlPathHelper) {
		Assert.hasText(pattern, "Pattern cannot be null or empty");
		this.caseSensitive = caseSensitive;
		if (pattern.equals(MATCH_ALL) || pattern.equals("**")) {
			pattern = MATCH_ALL;
			this.matcher = null;
		}
		else {
			// If the pattern ends with {@code /**} and has no other wildcards or path
			// variables, then optimize to a sub-path match
			if (pattern.endsWith(MATCH_ALL)
					&& (pattern.indexOf('?') == -1 && pattern.indexOf('{') == -1 && pattern.indexOf('}') == -1)
					&& pattern.indexOf("*") == pattern.length() - 2) {
				this.matcher = new SubpathMatcher(pattern.substring(0, pattern.length() - 3), caseSensitive);
			}
			else {
				this.matcher = new SpringAntMatcher(pattern, caseSensitive);
			}
		}
		this.pattern = pattern;
		this.httpMethod = StringUtils.hasText(httpMethod) ? HttpMethod.valueOf(httpMethod) : null;
		this.urlPathHelper = urlPathHelper;
	}

	/**
	 * Returns true if the configured pattern (and HTTP-Method) match those of the
	 * supplied request.
	 * @param request the request to match against. The ant pattern will be matched
	 * against the {@code servletPath} + {@code pathInfo} of the request.
	 */
	@Override
	public boolean matches(HttpServletRequest request) {
		if (this.httpMethod != null && StringUtils.hasText(request.getMethod())
				&& this.httpMethod != valueOf(request.getMethod())) {
			return false;
		}
		if (this.pattern.equals(MATCH_ALL)) {
			return true;
		}
		String url = getRequestPath(request);
		return this.matcher.matches(url);
	}

	...

	@Override
	public MatchResult matcher(HttpServletRequest request) {
		if (this.matcher == null || !matches(request)) {
			return MatchResult.notMatch();
		}
		String url = getRequestPath(request);
		return MatchResult.match(this.matcher.extractUriTemplateVariables(url));
	}

	private String getRequestPath(HttpServletRequest request) {
		if (this.urlPathHelper != null) {
			return this.urlPathHelper.getPathWithinApplication(request);
		}
		String url = request.getServletPath();
		String pathInfo = request.getPathInfo();
		if (pathInfo != null) {
			url = StringUtils.hasLength(url) ? url + pathInfo : pathInfo;
		}
		return url;
	}

	public String getPattern() {
		return this.pattern;
	}

	// 重写了 hasCode、equals、toString 方法

	// 定义了一个 Matcher 接口用于判断请求路径是否匹配,RequestMatcher 的入参是 HttpServletRequest
	private interface Matcher {

		boolean matches(String path);

		Map<String, String> extractUriTemplateVariables(String path);
	}

	// 下面是两个 Matcher 的实现类,
	private static final class SpringAntMatcher implements Matcher {
		...
	}

	/**
	 * Optimized matcher for trailing wildcards
	 */
	private static final class SubpathMatcher implements Matcher {
		...
	}

}

举例

在默认过滤器:UsernamePasswordAuthenticationFilter 中,定义了需要过滤的请求路径:

private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = 
			new AntPathRequestMatcher("/login", "POST");

表示这个过滤器会拦截 POST 请求方式的 /login 接口。查看抽象类 AbstractAuthenticationProcessingFilter

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
	// requiresAuthentication 判断请求是否需要认证
	if (!requiresAuthentication(request, response)) {
		chain.doFilter(request, response);
		return;
	}
	...
}

// 是否需要认证
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
	// 判断请求是否匹配
	if (this.requiresAuthenticationRequestMatcher.matches(request)) {
		return true;
	}
	if (this.logger.isTraceEnabled()) {
		this.logger
				.trace(LogMessage.format("Did not match request to %s", this.requiresAuthenticationRequestMatcher));
	}
	return false;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值