概述
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;
}