您的位置:首页 > 运维架构 > Apache

Apache commons 之 codec 深入学习以及URLCodec

2014-12-23 14:05 513 查看
我们在入门代码里,写了一些示例,感觉貌似这个类,就只有这么些内容,打开javadoc再看看,发现这个包里货还真不少。

Commons Codec 1.4 API
Packages
org.apache.commons.codec
A small set of interfaces used by the various implementations in the sub-packages.
org.apache.commons.codec.binary
Base64, Binary, and Hexadecimal String encoding and decoding.
org.apache.commons.codec.digest
Operations to simplifiy common MessageDigest tasks.
org.apache.commons.codec.language
Language and phonetic encoders.
org.apache.commons.codec.net
Network related encoding and decoding.
首先看看一些URL编码相关的知识。

简介

当我们每天上网冲浪时,有一些技术我们无时无刻不在面对。有数据本身(网页),数据的格式化,能够让我们获取数据的传输机制,以及让Web网络能够真正成为Web的基础及根本:从一页到另一页的链接。这些链接都是URL。

HTTP URL语法

对于HTTP URL (使用http 或 https 协议),URL的scheme描述部分定义了数据的路径(path),后面是可选的query 和fragment。

path 部分看上去是一个分层的结构,类似于文件系统中文件夹和文件的分层结构。path由"/"字符开始,每一个文件夹由"/"分隔,最后是文件。例如"/photos/egypt/cairo/first.jpg"有四个路径片段(segment):"photos"、"egypt"、"cairo" 和 "first.jpg",可以由此推出:"first.jpg"文件在文件夹"cairo"中,而"egypt"文件夹位于web站点的根文件夹"photos"里面。

URL 语法

http URL方案最初由 RFC 1738 定义(实际上,在之前的 RFC 1630也有涉及),而在 http URL
方案被重新定义之前,整个 URL 语法就已经由扩展几次 以适应发展的规范进化为一套 统一资源标识符(Uniform
Resource Identifiers 即 URIs)。

对于 URLs 如何拼装,各部分如何分隔有一套语法。例如:"://"分隔方案和主机部分。主机同路径片段部分由"/"分隔,而查询部分紧跟在"?"之后。这意味着有些字符为语法保留。有些为整个URIs保留,而有些则被特定方案保留。所有出现在不应出现位置的 保留符(例如路径片段——以文件名为例——可能包含"?")必须被URL 编码。

URL编码将字符转变成对 URL 解析无意义的无害形式。它将字符转化成为一种特定字符编码的字节序列,然后将字节转换为16进制形式,并将其前面加上"%"。问号的 URL 编码形式为"%3F"。

我们可以将指向 "to_be_or_not_to_be?.jpg"图片的 URL 写成:"http://example.com/to_be_or_not_to_be%3F.jpg",这样就没有人会认为这儿可能由一个查询部分了。

现今多数浏览器显示 URLs 前都会对其解码(将百分号编码字节转回其原本字符),并在获取其网络资源的时候重新编码。这样一来,很多用户从未意识到编码的存在。

另一方面,网页作者,开发者必须明确认识到这一点,因为这里存在着很多陷阱。

URL常见陷阱

如果你正和URL打交道,了解下能够避免的常见陷阱绝对是值得的。现在我们给大家介绍下不仅限于此的一些常见陷阱。

使用哪类字符编码?

URL编码规范并没有定义使用何种字符编码形式去编码字节。一般的ASCII字母数字字符并不需要转义,但是ASCII之外的保留字需要(例如法语单词“nœud”中的"œ")。我们必须提出疑问,应该使用哪类字符编码来编码URL字节。

当然如果只有Unicode的话,这个世界就会清净很多。因为每个字符都包含其中,但是它只是一个集合,或者说是列表如果你愿意,它本身并不是一中编码。Unicode可以使用多种方式进行编码,譬如UTF-8或者UTF-16(也有其它格式),但是问题并没有解决:我们应该使用哪类字符来编码URL(通常也指URI)。

标准并没有定义一个URI应该以何种方式指定其编码,所以其必须从环境信息中进行推导。对于HTTP URL,它可以是HTML页面的编码格式,或HTTP头的。这通常会让人迷惑,也是许多错误的根源。事实上,最新版的URI标准 定义了新的URI scheme将采用UTF-8,host(甚至已有的scheme)也使用UTF-8,这让我更加怀疑:难道host和path真的可以使用不同的编码方式?

每一部分的保留字都是不同。

是的,他们是,是的,他们,是的,他们是。。。

对于一个httpd连接,路径片段部分中的空格被编码为"%20"(不,完全没有"+"),而“+”字符在路径片段部分可以保持不编码。

现在,在查询部分,一个空格可能会被编码为“+”(为了向后兼容:不要试图在URI标准去搜索他)或者“%20”,当作为“+”字符(作为个统配符的结果)会被编译为“%2B”。

这意味着“blue+light blue”字串,如果在路径部分或者查询部分,将会有不同的编码。比如得到"http://example.com/blue+light%20blue?blue%2Blight+blue"这样的编码形式,这样我们不需从语法上分析url结构,就可以推导这个url的整个结构是可能

考虑如下组装URL的Java代码片段

?

1

2

String str =
"blue+light blue";


String url =
"
http://example.com/
"

+ str +
"?"
+ str;


编码URL并不是为了转义保留字而进行的简单字符迭代,我们需要确切的知道哪个URL部份有哪些保留字,而有针对性的进行编码。

这也意味着URL重写过滤器如果不考虑合适的编码细节而对URL直接进行分段转换通常是有问题的。对URL进行编码而不考虑具体的分段规则是不切实际的。

保留字不是你想象的那样

大多数人不知道"+"在路径部分是被允许的并且特指正号而不是空格。其他类似的有:

· "?"在查询部分允许不被转义,
· "/"在查询部分允许不被转义,
· "="在作为路径参数或者查询参数值以及在路径部分允许不被转义,
· ":@-._~!$&'()*+,;="等字符在路径部分允许不被转义,
· "/?:@-._~!$&'()*+,;="等字符在任何段中允许不被转义。
这样下面的地址虽然看起来有点混乱:"http://example.com/:@-._~!$&'()*+,=;:@-._~!$&'()*+,=:@-._~!$&'()*+,==?/?:@-._~!$'()*+,;=/?:@-._~!$'()*+,;==#/?:@-._~!$&'()*+,;="

按照上面的规则,其实上是一个合法的地址。

如果有兴趣的话,相关的知识有非常的多,从使用的角度简单归纳下:
1. 需要通过URL转码的字符,比如汉字等;
2. 不需要通过URL转码的字符,比如12*等;
3. 需要处理的保留字,比如 #等。
如果所有的字符都要自己处理,那么开发应用会比较复杂,而且非常枯燥,org.apache.commons.codec.net这个包就提供了这些数据的处理。
我们看源代码:
package test.ffm83.commons.codec;

import org.apache.commons.codec.CharEncoding;
import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
/**
* 通过apache commons
codec的net包进行url数据的加密
*
* @author范芳铭
*/
public
class
URLCodecUsage {
public
static void
main(String[] args)
throws Exception{
URLCodecUsage usage = new URLCodecUsage();
usage.useUrlEncode();
usage.useSafeCharEncodeDecode();
usage.useUnsafeEncodeDecode();
}

//普通字符的URL加密
public
void
useUrlEncode() throws Exception{
System.out.println(StringUtils.center("普通字符的URL加密",
50, "-"));
final String msg =
"范芳铭在做Url encode测试,123456";
final URLCodec urlCodec =
new URLCodec();
String enMsg =urlCodec.encode(msg, CharEncoding.UTF_8);
System.out.println(msg +
",加密之后");
System.out.println(enMsg);
}
//安全字符的编码解码
public
void
useSafeCharEncodeDecode() throws Exception {
System.out.println(StringUtils.center("安全字符的编码解码", 50,
"-"));
final URLCodec urlCodec =
new URLCodec();
final String plain =
"abc123_-.*";
//final String plain = "12345";
final String encoded = urlCodec.encode(plain);
System.out.println("plain:" + plain);
System.out.println("encoded:" + encoded);
System.out.println("Safe chars URL encoding test:"+ BooleanUtils.toStringYesNo(
plain.equals(encoded)));
System.out.println("Safe chars URL encoding test:"+ BooleanUtils.toStringYesNo(
plain.equals(urlCodec.decode(encoded))));
}

//不安全字符的编码解码
public
void
useUnsafeEncodeDecode()throwsException {
System.out.println(StringUtils.center("不安全字符的编码解码", 50,
"-"));
final URLCodec urlCodec =
new URLCodec();
final String plain =
"~!@#$%^&()+{}\"\\;:`,/[]";
final String encoded = urlCodec.encode(plain);
System.out.println("plain:" + plain);
System.out.println("encoded:" + encoded);
System.out.println("Safe chars URL encoding test:"+ BooleanUtils.toStringYesNo(
plain.equals(encoded)));
System.out.println("Safe chars URL encoding test:"+ BooleanUtils.toStringYesNo(
plain.equals(urlCodec.decode(encoded))));
}
}
运行后结果
--------------------普通字符的URL加密--------------------
范芳铭在做Urlencode测试,123456,加密之后
%E8%8C%83%E8%8A%B3%E9%93%AD%E5%9C%A8%E5%81%9AUrl+encode%E6%B5%8B%E8%AF%95%EF%BC%8C123456
--------------------安全字符的编码解码---------------------
plain:abc123_-.*
encoded:abc123_-.*
Safe chars URL encoding test:yes
Safe chars URL encoding test:yes
--------------------不安全字符的编码解码--------------------
plain:~!@#$%^&()+{}"\;:`,/[]
encoded:%7E%21%40%23%24%25%5E%26%28%29%2B%7B%7D%22%5C%3B%3A%60%2C%2F%5B%5D
Safe chars URL encoding test:no
Safechars URL encoding test:yes
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: