通配符 | 说明 |
---|---|
* | 匹配任意数量的字符 |
** | 匹配任意层级的路径/目录 |
? | 匹配任意单字符 |
看到没,这才比较符合咱们的习惯:*代表任意通配符才是正解嘛,而不是像正则一样代表匹配的数量来得让人“费解”。
**直接用于目录级别的匹配,可谓对URL这种字符串非常友好
最佳实践场景 正则表达式具有功能非常强大的特性,从理论上来讲,它可以用于任何场景,但是有些场景它并非最佳实践。
举个例子:在自定义的登录过滤器中,经常会放行一些API接口让免登录即可访问,这是典型的URL白名单场景,这个时候就会涉及到URL的匹配方式问题,一般会有如下方案:
精确匹配:url.equals(“/api/v1/yourbatman/adress”)。缺点:硬编码式一个个罗列,易造成错误且不好维护 前缀匹配:url.startsWith(“/api/v1/yourbatman”)。这也算一种匹配模式,可以批量处理某一类URL。缺点是:匹配范围过大易造成误伤,或者范围过小无法形成有效匹配,总之就是欠缺灵活度 包含匹配:url.contains(“/yourbatman”)。这个缺点比较明显:强依赖于URL的书写规范(如白名单的URL都必须包含指定子串),并且极易造成误伤 正则表达式匹配:Pattern.compile(“正则表达式”)…matcher(url).find()。它的最大优点是可以满足几乎任意的URL(包括精确、模式等),但最大的缺点是书写比较复杂,用时多少这和coder的水平强相关,另外这对后期维护也带来了一定挑战~ 经常会听到这样一句话:“通过正则表达式或者Ant风格的路径表达式来做URL匹配”。正所谓“杀鸡何必用牛刀”,URL相较于普通的字符串具有很强的规律性:标准的分段式。因此,使用轻量级Ant风格表达式作为URL的匹配模式更为合适:
轻量级执行效率高 通配符(模式)符合正常理解,使用门槛非常低 *和**对层级路径/目录的支持感觉就是为此而生的 对于复杂场景亦可包含正常表达式来达到通用性 总的来讲,所谓为谁更好。Ant风格和正则表达式都有它们场景的最佳实践:
Ant风格:用于URL/目录这种标准分段式路径匹配 正则表达式:用于几乎没规律(或者规律性不强)的普通字符串匹配 AntPathMatcher:基于Ant风格的路径匹配器 PathMatcher接口并未规定路径匹配的具体方式,在Spring的整个技术栈里(包括Spring Boot和Cloud)有且仅有一个实现类AntPathMatcher:基于Ant风格的路径匹配器。它运用在Spring技术栈的方方面面,如:URL路径匹配、资源目录匹配等等。
这里有个有趣的现象:AntPathMatcher是Since:16.07.2003,而其接口PathMatcher是Since:1.2(2005.12)整整晚了2年+才出现。Spring当初的设想是把路径匹配抽象成为一种模式(也就是PathMatcher)而不限定具体实现,但奈何近20年过去了AntPathMatcher仍旧为PathMatcher接口的唯一实现。
说明:Spring 5新增了更高效的、设计更好的、全新的路径匹配器PathPattern,但它并未实现PathMatcher接口而是一套全新“生态”,用于逐步替换掉AntPathMatcher。关于此,下篇文章有详尽分析
说一千,道一万。了解PathMatcher/AntPathMatcher最为重要的是什么?当然是了解它的匹配规则,做到心里有数。安排,下面我就通过代码示例方式演示其匹配,尽量做到全乎,让你一文在手全部都有
public static void main(String[] args) {
PathMatcher matcher = new AntPathMatcher();
String pattern = "/xxx/**/*.txt";
System.out.println(matcher.match(pattern, "/xxx/hello.txt")); // true
System.out.println(matcher.match(pattern, "/xxx/yyy/hello.txt"));// true
System.out.println(matcher.match(pattern, "/aaa/xxx/yyy/hello.txt")); //false
}
值得注意的:
在配置白名单是不能加context-path(猜测是spring帮忙加了,加上匹配不对)
// 请求白名单 登录
public static String URL_WHITE_ARRAY[];
// 请求白名单 验证token 自定义过滤器
public static List<UrlWhite> URL_WHITELIST = new ArrayList<>();
static {
URL_WHITELIST.add(new UrlWhite("/captcha"));
URL_WHITELIST.add(new UrlWhite("/login"));
URL_WHITELIST.add(new UrlWhite("/logout"));
URL_WHITELIST.add(new UrlWhite("/password"));
URL_WHITELIST.add(new UrlWhite("/image/**"));
URL_WHITELIST.add(new UrlWhite("/sysUser/registerUser"));
URL_WHITELIST.add(new UrlWhite("/hello/**"));
URL_WHITELIST.add(new UrlWhite("/blogArticle/**", "GET"));
URL_WHITELIST.add(new UrlWhite("/blogArticle/getBlogArticleListByPage"));
URL_WHITELIST.add(new UrlWhite("/blogCategory/**", "GET"));
URL_WHITELIST.add(new UrlWhite("/blogCategory/getBlogCategoryListByPage"));
URL_WHITELIST.add(new UrlWhite("/blogTag/**", "GET"));
URL_WHITELIST.add(new UrlWhite("/blogTag/getBlogTagListByPage"));
URL_WHITE_ARRAY = URL_WHITELIST.stream().map(item -> item.getUrl()).toArray(size -> new String[size]);
}
如果自己写需要加上context-path
// /dbd/blogTag/myBlogTagList
String requestURI = request.getRequestURI();
AntPathMatcher matcher = new AntPathMatcher();
for (UrlWhite whiteUrl : GlobalConstant.URL_WHITELIST) {
if (matcher.match(GlobalConstant.CONTEXT_PATH + whiteUrl.getUrl(), requestURI)) {
}
}