需求描述: 要对路径进行白名单过滤,满足通配符**
/**
* 路径匹配
* @param pattern
* @param url
* @return true : 能匹配 false: 不匹配
*/
static pathMatch(pattern: string, url: string) {
const path = new URL(url).pathname;
if (pattern === path) {
return true;
}
// 判断开头字符
if (pattern.startsWith(this.pathSeparator) !== path.startsWith(this.pathSeparator)) {
return false;
}
const pattDirs = pattern.split(this.pathSeparator);
const pathDirs = path.split(this.pathSeparator);
let pattIdxStart = 0;
let pattIdxEnd = pattDirs.length - 1;
let pathIdxStart = 0;
let pathIdxEnd = pathDirs.length - 1;
while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
let pattDir = pattDirs[pattIdxStart];
if ("**" === pattDir) {
break;
}
if ("*" === pattDir) {
pattIdxStart++;
pathIdxStart++;
continue;
}
if (pattDir !== pathDirs[pathIdxStart]) {
return false;
}
pattIdxStart++;
pathIdxStart++;
}
if (pathIdxStart > pathIdxEnd) {
// Path is exhausted, only match if rest of pattern is * or **'s
if (pattIdxStart > pattIdxEnd) {
return (pattern.endsWith(this.pathSeparator) == path.endsWith(this.pathSeparator));
}
if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart] === "*" && path.endsWith(this.pathSeparator)) {
return true;
}
for (let i = pattIdxStart; i <= pattIdxEnd; i++) {
if (pattDirs[i] !== "**") {
return false;
}
}
return true;
} else if (pattIdxStart > pattIdxEnd) {
// String not exhausted, but pattern is. Failure.
return false;
} else if ("**" === pattDirs[pattIdxStart]) {
// Path start definitely matches due to "**" part in pattern.
return true;
}
while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
let pattDir = pattDirs[pattIdxEnd];
if (pattDir === "**") {
break;
}
if ("*" === pattDir) {
pattIdxStart++;
pathIdxStart++;
continue;
}
if (pattDir !== pathDirs[pathIdxEnd]) {
return false;
}
pattIdxEnd--;
pathIdxEnd--;
}
if (pathIdxStart > pathIdxEnd) {
// String is exhausted
for (let i = pattIdxStart; i <= pattIdxEnd; i++) {
if (pattDirs[i] !== "**") {
return false;
}
}
return true;
}
while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
let patIdxTmp = -1;
for (let i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
if (pattDirs[i] === "**") {
patIdxTmp = i;
break;
}
}
if (patIdxTmp == pattIdxStart + 1) {
// '**/**' situation, so skip one
pattIdxStart++;
continue;
}
// Find the pattern between padIdxStart & padIdxTmp in str between
// strIdxStart & strIdxEnd
let patLength = (patIdxTmp - pattIdxStart - 1);
let strLength = (pathIdxEnd - pathIdxStart + 1);
let foundIdx = -1;
strLoop:
for (let i = 0; i <= strLength - patLength; i++) {
for (let j = 0; j < patLength; j++) {
let subPat = pattDirs[pattIdxStart + j + 1];
let subStr = pathDirs[pathIdxStart + i + j];
if (subPat !== subStr) {
continue strLoop;
}
}
foundIdx = pathIdxStart + i;
break;
}
if (foundIdx == -1) {
return false;
}
pattIdxStart = patIdxTmp;
pathIdxStart = foundIdx + patLength;
}
for (let i = pattIdxStart; i <= pattIdxEnd; i++) {
if (pattDirs[i] !== "**") {
return false;
}
}
return true;
}
使用示例
pathMatch("/**", "http://aaa.bb.com/aaa/bbb") // true
pathMatch("/a/*/b", "http://aaa.bb.com/a/ccc/b") // true
pathMatch("/a/b/c", "http://aaa.bb.com/a/b/c") // true