Spring-Security-实战干货路径Uri中的-Ant-风格

1. 前言我们经常在读到一些文章会遇到uri 支持 Ant 风格 ,而且这个东西在 Spring MVC 和 Spring Security 中经常被提及。这到底是什么呢?今天我们来学习了解一下。这对我们学习 Spring MVC 和 Spring Security 十分必要。 2. Ant 风格说白了 Ant 风格就是一种路径匹配表达式。主要用来对uri的匹配。其实跟正则表达式作用是一样的,只不过正则表达式适用面更加宽泛,Ant仅仅用于路径匹配。 3. Ant 通配符Ant 中的通配符有三种: ? 匹配任何单字符* 匹配0或者任意数量的 字符** 匹配0或者更多的 目录这里注意了单个* 是在一个目录内进行匹配。 而** 是可以匹配多个目录,一定不要迷糊。 3.1 Ant 通配符示例通配符示例说明?/ant/p?ttern匹配项目根路径下 /ant/pattern 和 /ant/pXttern,但是不包括/ant/pttern*/ant/*.html匹配项目根路径下所有在ant路径下的.html文件*/ant/*/path/ant/path、/ant/a/path、/ant/bxx/path 都匹配,不匹配 /ant/axx/bxx/path**/ant/**/path/ant/path、/ant/a/path、/ant/bxx/path 、/ant/axx/bxx/path都匹配3.2 最长匹配原则从 3.1 可以看出 * 和 ** 是有冲突的情况存在的。为了解决这种冲突就规定了最长匹配原则(has more characters)。 一旦一个uri 同时符合两个Ant匹配那么走匹配规则字符最多的。为什么走最长?因为字符越长信息越多就越具体。比如 /ant/a/path 同时满足 /**/path 和 /ant/*/path 那么走/ant/*/path 4. Spring MVC 和 Spring Security 中的 Ant 风格接下来我们来看看 Spring MVC 和 Spring Security 下的 Ant风格。 ...

October 15, 2019 · 1 min · jiezi

new URI(zk_servers_1) 路径包含下划线无法获取host的问题

spring cloud gateway使用zookeeper作为注册中心调用其它服务的时候报了下面这个错误:ava.lang.NullPointerException: null at io.netty.util.NetUtil.isValidIpV4Address(NetUtil.java:648) ~[netty-common-4.1.29.Final.jar:4.1.29.Final] at io.netty.util.NetUtil.createByteArrayFromIpAddressString(NetUtil.java:368) ~[netty-common-4.1.29.Final.jar:4.1.29.Final] at reactor.ipc.netty.options.InetSocketAddressUtil.attemptParsingIpString(InetSocketAddressUtil.java:132) ~[reactor-netty-0.7.10.RELEASE.jar:0.7.10.RELEASE] at reactor.ipc.netty.options.InetSocketAddressUtil.createForIpString(InetSocketAddressUtil.java:80) ~[reactor-netty-0.7.10.RELEASE.jar:0.7.10.RELEASE] at reactor.ipc.netty.options.InetSocketAddressUtil.createInetSocketAddress(InetSocketAddressUtil.java:69) ~[reactor-netty-0.7.10.RELEASE.jar:0.7.10.RELEASE] at reactor.ipc.netty.options.ClientOptions.createInetSocketAddress(ClientOptions.java:253) ~[reactor-netty-0.7.10.RELEASE.jar:0.7.10.RELEASE] at reactor.ipc.netty.http.client.HttpClientOptions.getRemoteAddress(HttpClientOptions.java:87) ~[reactor-netty-0.7.10.RELEASE.jar:0.7.10.RELEASE] at reactor.ipc.netty.http.client.MonoHttpClientResponse.lambda$subscribe$0(MonoHttpClientResponse.java:76) ~[reactor-netty-0.7.10.RELEASE.jar:0.7.10.RELEASE]调用的地址是http://zks_servers_1:18001,zks_servers_1是服务的hosts配置的名称,空指针异常跟踪发现是下面这个问题导致的://类HttpClientOptions public final InetSocketAddress getRemoteAddress(URI uri) { Objects.requireNonNull(uri, “uri”); boolean secure = isSecure(uri); int port = uri.getPort() != -1 ? uri.getPort() : (secure ? 443 : 80); boolean shouldResolveAddress = !this.useProxy(uri.getHost()); return this.createInetSocketAddress(uri.getHost(), port, shouldResolveAddress); }uri.getHost()返回值是null,也就是说根据上面的调用地址,没有获取到对应的host。uri的创建方式是://类MonoHttpClientResponseMonoHttpClientResponse(HttpClient parent, String url, HttpMethod method, Function<? super HttpClientRequest, ? extends Publisher<Void>> handler) { this.parent = parent; boolean isWs = Objects.equals(method, HttpClient.WS); try { this.startURI = new URI(parent.options.formatSchemeAndHost(url, isWs)); } catch (URISyntaxException var7) { throw Exceptions.bubble(var7); } this.method = isWs ? HttpMethod.GET : method; this.handler = handler; }创建方式是调用URI的new URI(String)方法,知道原因之后在本地测试:try { URI uri = new URI(“http://zks_servers_1:18001/test.html”); String host = uri.getHost(); System.out.println(host); } catch (URISyntaxException e) { e.printStackTrace(); }确实获取不到host,查看源码发现: /** * Returns the host component of this URI. * * <li><p> A domain name consisting of one or more <i>labels</i> * separated by period characters ({@code ‘.’}), optionally followed by * a period character. Each label consists of <i>alphanum</i> characters * as well as hyphen characters ({@code ‘-’}), though hyphens never * occur as the first or last characters in a label. The rightmost * label of a domain name consisting of two or more labels, begins * with an <i>alpha</i> character. </li> * </ul> * * The host component of a URI cannot contain escaped octets, hence this * method does not perform any decoding. * * @return The host component of this URI, * or {@code null} if the host is undefined */ public String getHost() { return host; }谷歌翻译:由一个或多个标签组成的域名 由句点字符代码’.‘分隔,可选地后跟 一个英文句号角色。 每个标签由alphanum字符组成 以及连字符字符代码’ - ‘,虽然连字符永远不会 作为标签中的第一个或最后一个字符出现。 最右边包含最少长度最少两个并且以英文字符开始的标签举例如下:www.baidu.com,这个域名包含三个标签www、baidu、com;www.baidu-zhidao.com,这个域名包含三个标签www、baidu-zhidao、com;这个例子就是说明每个标签都可以使用-连接;然后看一下,我的报错的服务名称zks_servers_1,这个名称没有以【.】分割,包含了非法字符【_】最后以单个数字结尾也不符合要求。实际上测试发现zks-servers-1这样也是不正确的,不知道是不是翻译的有问题,这个结果和翻译不太匹配。总而言之,修改服务器的hosts配置就行了。 ...

February 28, 2019 · 2 min · jiezi

使用Data URI Scheme优雅的实现前端导出csv

问题描述项目里需要实现一个导出csv的功能,这是个老生常谈的需求,而且我们使用的是iview的组件库,按道理说实现起来应该简单,但实则不然,我在做的时候遇到了一些问题。受限于请求需要token、后端分页、接口、性能等原因不得不放弃iview的导出方式。所以我需要寻找一种可行的、合理的、优雅的导出方案,那就是Data URI Scheme。方案实现方案介绍Data URI Scheme是利用HTML标签的href和src属性来实现的。他看起来像是这样的:<img src=“data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==” alt=“Red dot” />或者<a href=“data:text/csv,something”>download</a>按照这种方案的介绍,我们把要导出的数据拼接在href指定位置就能实现导出的需求,代码实现看起来像这样:<a href="" download=“export.csv” id=“export_csv” style=“display=‘none’">download</a>function export_csv (data) { $(’#export_csv’).href = ‘data:attachment/csv,’ + encodeURI(data); $(’#export_csv’).click(); setTimeout(function () { $(’#export_csv’).href = ‘’; })}export_csv(csv_data_str);测试发现,妥妥的,没毛病。存在问题但在实践中这个方案是有限制的:在chrome的实现中这个url最大限制为2MB。所以,当在Chrome下载的文件大小超过2MB chrome便会报这样的错误(其他浏览器这里不做讨论):下载失败-网络错误这里2MB的大小可以在chromium源码中可以看到:const size_t kMaxURLChars = 2 * 1024 * 1024;…if (!iter->ReadString(&s) || s.length() > url::kMaxURLChars) { *p = GURL(); return false;}变量声明部分源码链接变量引用部分源码链接而关于2MB限制的问题在chromium论坛在2010年就被人作为bug提出来了,但是从2010年一直讨论到2019年也没有明显的改善。方案改进chromium不改,那我们只能自己想办法了,于是有大牛提出来使用URL.createObjectURL + Blob来突破这个限制。借助Blob对象和URL.createObjectURL我们可以得到形如下面的URL:blob:https://xxx.com/0bde569d-20a2-4085-95e6-dcec242962c6这样就能突破Chrome对Data URI Scheme URL大小的限制了。当然呢,我们没用过URL.createObjectURL这个方法,也没用过Blob对象,所以我们要看看浏览的支持情况恩,看起来没有问题,那我们来看看代码实现。<a href=”" download=“export.csv” id=“export_csv” style=“display=‘none’">download</a>function export_csv (data) { const BOM = ‘\uFEFF’; let blob_obj = new Blob([BOM + data], {type: ’text/csv’}); let download_url = URL.createObjectURL(blob_obj); $(’#export_csv’).href = download_url; $(’#export_csv’).click(); setTimeout(function () { // 通过createObjectURL创建的url需要通过revokeObjectURL()来释放 URL.revokeObjectURL(download_url); $(’#export_csv’).href = ‘’; })}export_csv(csv_data_str);恩,这样就不怕超过2MB的CSV的导出了,但是Blob对象有大小限制吗?Good question !我们在chromium的说明文档中可以看到一个表:DeviceRamIn-Memory LimitDiskDisk LimitMin Disk AvailabilityCast512 MB102 MB000Android Minimal512 MB5 MB8 GB491 MB10 MBAndroid Fat2 GB20 MB32 GB1.9 GB40 MBCrOS2 GB409 MB8 GB4 GB0.8 GBDesktop 323 GB614 MB500 GB50 GB1.2 GBDesktop 644 GB2 GB500 GB50 GB4 GB从这个表中,大概可以看出来在In-Memory Storage的时候桌面版64位Chrome Blob的上限为2GB(在Chrome 57似乎上限是500MB)。所以从现在看来这种方法应该是安全的。至此,这个问题算是完整的解决了。iview的实现另外,在我写这篇文章的时候我发现iview的export-csv方法也是按照这个方案实施的,而且做了更多兼容,可以方便大家参考。但他在资源释放的地方做的还需改进,也希望大家注意。参考文档Data protocol URL size limitationsExcellent Export and the Chrome URL limitData_URI_schemeexcellentexport pull request无法在nodejs中下载大文件Issue 69227: Loading large URLs kills the rendererIssue 375297: the total blobs’ size cannot exceed about 500MiBIs there any limitation on JavaScript Max Blob sizechromium/url/url_param_traits.cc#L36chromium/url/url_constants.cc#L32iview 3.x export-csv ...

February 17, 2019 · 1 min · jiezi