关于在网关中白名单配置的使用(AntPathMatcher)

关于在网关中白名单配置的使用

在项目里,经常会有需要添加白名单,黑名单的功能, 而一般的处理方式,都是在网关进行设置配置, Spring中提供的工具包类AntPathMatcher可以很方便处理.

1 说明

Spring为PathMatcher接口提供了默认实现类,即AntPathMatcher类. 且支持Ant风格路径匹配.

通配符说明
?匹配一个字符
*匹配0个或多个字符
**匹配0个或多个目录

此外,AntPathMatcher还支持{}参数匹配.

// true
antPathMatcher.match("/root/opt","/root/opt"); 

代码查看:

PathMatcher

public interface PathMatcher {
    // 判断路径是否带模式
    boolean isPattern(String path);
	// 判断path是否完全匹配
    boolean match(String pattern, String path);
    // 判断path是否前缀匹配
    boolean matchStart(String pattern, String path);
    // 去掉路径开头的静态部分, 得到匹配的动态路径
    String extractPathWithinPattern(String pattern, String path);

    // 匹配路径中的变量
    Map<String, String> extractUriTemplateVariables(String pattern, String path);

    // 返回一个排序比较器,可对路径进行排序
    Comparator<String> getPatternComparator(String path);

    // 合并两个模式
    String combine(String pattern1, String pattern2);
}

AntPathMatcher

public class AntPathMatcher implements PathMatcher {
    public static final String DEFAULT_PATH_SEPARATOR = "/";
    private static final int CACHE_TURNOFF_THRESHOLD = 65536;
    private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?\\}");
    private static final char[] WILDCARD_CHARS = new char[]{'*', '?', '{'};
    private String pathSeparator;
    private AntPathMatcher.PathSeparatorPatternCache pathSeparatorPatternCache;
    private boolean caseSensitive = true;
    private boolean trimTokens = false;
    @Nullable
    private volatile Boolean cachePatterns;
    private final Map<String, String[]> tokenizedPatternCache = new ConcurrentHashMap(256);
    final Map<String, AntPathMatcher.AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap(256);

    public AntPathMatcher() {
        this.pathSeparator = "/";
        this.pathSeparatorPatternCache = new AntPathMatcher.PathSeparatorPatternCache("/");
    }
    // ... 省略
}

从上面构造可以得知, AntPathMatcher默认路径分隔符为“/”,而在匹配文件路径时,需要注意Windows下路径分隔符为“\”, Linux下为“/”.(所以在不同的操作系统来传递各自的文件分隔符)

isPattern方法

判断路径中是否有*或?,{, }, 存在则说明是模式,返回true,否则返回false.

    public boolean isPattern(@Nullable String path) {
        if (path == null) {
            return false;
        } else {
            boolean uriVar = false;

            for(int i = 0; i < path.length(); ++i) {
                char c = path.charAt(i);
                if (c == '*' || c == '?') {
                    return true;
                }

                if (c == '{') {
                    uriVar = true;
                } else if (c == '}' && uriVar) {
                    return true;
                }
            }

            return false;
        }
    }

match,matchStart,extractUriTemplateVariables方法

上面三个方法, 最后都调用到doMatch方法.

    // match完全匹配
    public boolean match(String pattern, String path) {
        return this.doMatch(pattern, path, true, (Map)null);
    }
    // 前缀匹配
    public boolean matchStart(String pattern, String path) {
        return this.doMatch(pattern, path, false, (Map)null);
    }

    // 去掉路径开头的静态部分, 得到匹配的动态路径
    public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
        Map<String, String> variables = new LinkedHashMap();
        boolean result = this.doMatch(pattern, path, true, variables);
        if (!result) {
            throw new IllegalStateException("Pattern \"" + pattern + "\" is not a match for \"" + path + "\"");
        } else {
            return variables;
        }
    }

doMatch方法

    /**
     * @param pattern   模式
     * @param path      路径
     * @param fullMatch 是否需要完全匹配
     * @param uriTemplateVariables 收集路径中的参数
     */
    protected boolean doMatch(String pattern, @Nullable String path, boolean fullMatch, @Nullable Map<String, String> uriTemplateVariables) {
        if (path != null && path.startsWith(this.pathSeparator) == pattern.startsWith(this.pathSeparator)) {
            String[] pattDirs = this.tokenizePattern(pattern);
            if (fullMatch && this.caseSensitive && !this.isPotentialMatch(path, pattDirs)) {
                return false;
            } else {
                String[] pathDirs = this.tokenizePath(path);
                int pattIdxStart = 0;
                int pattIdxEnd = pattDirs.length - 1;
                int pathIdxStart = 0;
           // ...省略
    }
            
            
    protected String[] tokenizePattern(String pattern) {
        String[] tokenized = null;
        Boolean cachePatterns = this.cachePatterns;
        if (cachePatterns == null || cachePatterns) {
            tokenized = (String[])this.tokenizedPatternCache.get(pattern);
        }

        if (tokenized == null) {
            tokenized = this.tokenizePath(pattern);
            if (cachePatterns == null && this.tokenizedPatternCache.size() >= 65536) {
                this.deactivatePatternCache();
                return tokenized;
            }

            if (cachePatterns == null || cachePatterns) {
                this.tokenizedPatternCache.put(pattern, tokenized);
            }
        }

        return tokenized;
    }
            
            
     protected String[] tokenizePath(String path) {
        return StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true);
    }

tokenizePattern 方法最终也是调用 tokenizePath 方法,是在其基础上加了一层缓存。 tokenizePath 方法则是直接调用工具方法 tokenizeToStringArray 方法,与 String.split() 方法效果是差不多的。

2 案例

    // 设置成可配置  一般从nacos配置中获取 
    List<String> whiteList = new ArrayList<>();

    /**
     *
     * @param path
     * @return  为空表示 白名单 放过
     */
    @Nullable
    public AntPathMatcher checkWhitelList(String path){
        AntPathMatcher matcher = new AntPathMatcher();

        for (String white : whiteList) {
            if (matcher.match(white,path)){
                return null;
            }
        }
        return matcher;
    }

3 说明

对于系统中添加白名单,黑名单等匹配功能, 可以使用Spring中提供的AntPathMatcher.使用比较灵活方便,其代码实现也直观.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值