Base64编码是从二进制值到某些特定字符的编码,这些特定字符一共64个,所以称作Base64。所以其实质是将二进制数据转换成文本数据的方案;对于非二进制数据,则是把它转换成二进制,再每连续6比特(2^6)计算其十进制值,根据该值在上面的索引表中找到对应字符,最终得到一个文本字符串。
每3个原始字符经Base64编码成4个字符,编码前后长度比4/3。如果原始字符串长度不能被3整除,那使用0值来补充原始字符串。
为什么不直接传输二进制呢?比如图片,或者字符,既然实际传输时它们都是二进制字节流。而且即使Base64编码过的字符串最终也是二进制(通常是UTF-8编码,兼容ASCII编码)在网络上传输的,那么用4/3倍带宽传输数据的Base64究竟有什么意义?
真正的原因是二进制不兼容。某些二进制值,在一些硬件上,比如在不同的路由器,老电脑上,表示的意义不一样,做的处理也不一样。同样,一些老的软件,网络协议也有类似的问题。
但是万幸,Base64使用的64个字符,经ASCII/UTF-8编码后在大多数机器,软件上的行为是一样的。
Hello!!
Base64编码的结果为 SGVsbG8hIQAA
。最后2个零值只是为了Base64编码而补充的,所以实际不带有效信息,所以需要特殊处理,以免解码错误:用=
字符来替换最后的 A
,即编码结果为 SGVsbG8hIQ==
( =
字符并不在Base64编码索引表中,其意义在于结束符号)解码时如果发现Base64编码字符串长度不能被4整除,则先补充 =
字符,再解码即可。
解码是对编码的逆向操作,但注意一点:对于最后的两个 =
字符,转换成两个 A
字符,再转成对应的两个6比特二进制0值,接着转成原始字符之前,需要将最后的两个6比特二进制0值丢弃(因为它们实际上不携带有效信息)。
HTML内嵌Base64编码图片
前端在实现页面时,对于一些简单图片,通常会选择将图片内容直接内嵌在页面中,避免不必要的外部资源加载,增大页面加载时间,但是图片数据是二进制数据,该怎么嵌入呢?绝大多数现代浏览器都支持一种名为 Data URLs
的特性,允许使用Base64对图片或其他文件的二进制数据进行编码,将其作为文本字符串嵌入网页中。以百度搜索首页为例,其中语音搜索的图标是个背景图片,其内容以 Data URLs
形式直接写在css中,这个css内容又直接嵌在HTML页面。但是不适用于图片较大色彩层次比较丰富的情景,因为其Base64编码后的字符串非常大,会明显增大HTML页面,影响加载速度。
切忌误用于数据加密或数据校验。
Base64是一种数据编码方式,目的是让数据符合传输协议的要求。标准Base64编码解码无需额外信息即完全可逆,即使你自己自定义字符集设计一种类Base64的编码方式用于数据加密,在多数场景下也较容易破解。
对于数据加密应该使用专门的目前还没有有效方式快速破解的加密算法。比如:对称加密算法AES-128-CBC
,对称加密需要密钥,只要密钥没有泄露,通常难以破解;也可以使用非对称加密算法,如 RSA
,利用极大整数因数分解的计算量极大这一特点,使得使用公钥加密的数据,只有使用私钥才能快速解密。
对于数据校验,也应该使用专门的消息认证码生成算法,如 HMAC
- 一种使用单向散列函数构造消息认证码的方法,其过程是不可逆的、唯一确定的,并且使用密钥来生成认证码,其目的是防止数据在传输过程中被篡改或伪造。将原始数据与认证码一起传输,数据接收端将原始数据使用相同密钥和相同算法再次生成认证码,与原有认证码进行比对,校验数据的合法性。
那么针对各大网站被脱库的问题,请问应该怎么存储用户的登录密码?
答案是:在注册时,根据用户设置的登录密码,生成其消息认证码,然后存储用户名和消息认证码,不存储原始密码。每次用户登录时,根据登录密码,生成消息认证码,与数据库中存储的消息认证码进行比对,以确认是否为有效用户,这样即使网站被脱库,用户的原始密码也不会泄露,不会为用户使用的其他网站带来账号风险。
当然,使用的消息认证码算法其哈希碰撞的概率应该极低才行,目前一般在HMAC算法中使用SHA256。对于这种方式需要注意一点:防止用户使用弱密码,否则也可能会被暴力破解。现在的网站一般要求用户密码6个字符以上,并且同时有数字和大小写字母,甚至要求有特殊字符。
另外,也可以使用加入随机salt的哈希算法来存储校验用户密码。这里暂不细述。
依赖
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
<scope>compile</scope>
</dependency>