jdk1.8 base64注意事项

由于jdk1.7和jdk1.8内置的Base64遵守的RFC协议不一致,jdk1.7按照照RFC1521实现的,jdk1.8是按照rfc4648和rfc2045两个协议来实现的。具体可以从类注释中查询到。由于协议的不同可能导致jdk1.8的解码jdk1.7编码的数据时抛出java.lang.IllegalArgumentException: Illegal base64 character a异常.因此需要特别注意保持解码编码的一致性。

jdk7的编码结果包含换行
jdk8的编码结果不包含换行
jdk8无法解码包含换行的编码结果;

既然得知上述异常产生的由于,故找到解决方案也很简单。

  1. 使用apache common包中的org.apache.commons.codec.binary.Base64类进行编码和解码;
  2. 编码之后或解码之前去除换行符;
  3. 编码和解码使用相同的jdk版本

-----------------------------------------------------------------------------------------

概述

Base64是一种字符串编码格式,采用了A-Z,a-z,0-9,“+”和“/”这64个字符来编码原始字符(还有垫字符“=”)。一个字符本身是一个字节,也就是8位,而base64编码后的一个字符只能表示6位的信息。也就是原始字符串中的3字节的信息编码会变成4字节的信息。Base64的主要作用是满足MIME的传输需求。 
在Java8中Base64编码已经成为Java类库的标准,且内置了Base64编码的编码器和解码器。

问题

偶然发现使用jdk8内置的Base64解码器进行解析的时候,会抛出java.lang.IllegalArgumentException: Illegal base64 character a异常。 
这非常奇怪,因为原文是使用jdk7里面的编码器进行编码的,理论上不至于发生这种不兼容的状况。

测试程序

还是来写程序测试一下问题到底在哪里。

测试程序使用了一个比较长的原文,主要是这个问题在原文较长的时候才会出现,如果原文较短(字节长度不超过57),那么不会有这个问题。

使用jdk7进行编码:

import sun.misc.BASE64Encoder;
public class TestBase64JDK7 {private static final String TEST_STRING = "0123456789,0123456789,0123456789,0123456789,0123456789,0123456789,0123456789";public static void main(String[] args) {BASE64Encoder base64Encoder = new BASE64Encoder();String base64Result = base64Encoder.encode(TEST_STRING.getBytes());System.out.println(base64Result);}
}

jdk7编码结果:

MDEyMzQ1Njc4Oe+8jDAxMjM0NTY3ODnvvIwwMTIzNDU2Nzg577yMMDEyMzQ1Njc4Oe+8jDAxMjM0
NTY3ODnvvIwwMTIzNDU2Nzg577yMMDEyMzQ1Njc4OQ==

使用jdk8对上面的编码结果进行解码:

import java.util.Base64;
public class TestBase64JDK8 {public static void main(String[] args) {String base64Result = "MDEyMzQ1Njc4Oe+8jDAxMjM0NTY3ODnvvIwwMTIzNDU2Nzg577yMMDEyMzQ1Njc4Oe+8jDAxMjM0\n" +"NTY3ODnvvIwwMTIzNDU2Nzg577yMMDEyMzQ1Njc4OQ==";Base64.getDecoder().decode(base64Result);}
}

结果就如最开始描述的那样,会抛出异常:

Exception in thread "main" java.lang.IllegalArgumentException: Illegal base64 character aat java.util.Base64$Decoder.decode0(Base64.java:714)at java.util.Base64$Decoder.decode(Base64.java:526)at java.util.Base64$Decoder.decode(Base64.java:549)at com.francis.TestBase64JDK8.main(TestBase64JDK8.java:14)

难道说jdk7和jdk8在base64的处理上有什么不一样???

继续来看一下jdk8对原文的编码:

import java.util.Base64;
public class TestBase64JDK8 {private static final String TEST_STRING = "0123456789,0123456789,0123456789,0123456789,0123456789,0123456789,0123456789";public static void main(String[] args) {String base64Result = Base64.getEncoder().encodeToString(TEST_STRING.getBytes());System.out.println(base64Result);}
}

jdk8编码结果:

MDEyMzQ1Njc4Oe+8jDAxMjM0NTY3ODnvvIwwMTIzNDU2Nzg577yMMDEyMzQ1Njc4Oe+8jDAxMjM0NTY3ODnvvIwwMTIzNDU2Nzg577yMMDEyMzQ1Njc4OQ==

至此针对比较长的原文进行base64编码可以得到如下结论:

  • jdk7的编码结果包含换行;
  • jdk8的编码结果不包含换行;
  • jdk8无法解码包含换行的编码结果;
jdk8的编码结果使用jdk7进行解码,没有任何问题,不再演示。

现在问题原因基本清楚了,是由于jdk7的编码结果包含换行,导致jdk8解码的时候抛出异常。 
但是为什么会有这种差异呢?难道使用的base64的标准还不一样?

问题排查

继续排查问题,先从类注释入手,看看是不是理解有误。

1 先来看看jdk8中的Base64类注释,这里只列出一些关键内容:

/*** This class consists exclusively of static methods for obtaining* encoders and decoders for the Base64 encoding scheme. The* implementation of this class supports the following types of Base64* as specified in* <a href="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</a> and* <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>.** <ul>* <li><a name="basic"><b>Basic</b></a>* <p> Uses "The Base64 Alphabet" as specified in Table 1 of*     RFC 4648 and RFC 2045 for encoding and decoding operation.*     The encoder does not add any line feed (line separator)*     character. The decoder rejects data that contains characters*     outside the base64 alphabet.</p></li>...* @author  Xueming Shen* @since   1.8*/

大意是说:

这个类包含了base64编码格式的编码方法和解码方法,而且实现是按照rfc4648和rfc2045两个协议来实现的。
编码和解码操作是照着两个协议中的'Table 1'中指定的'The Base64 Alphabet'来的。编码器不会添加任何换行符,解码器只会处理'The Base64 Alphabet'范围内的数据,如果不在这个范围内,解码器会拒绝处理。

看到这里就可以理解为什么jdk8的编码结果不包含换行了。 

另外,基本上可以猜到为什么jdk8无法解码jdk7的编码结果了(换行符应该不在The base64 alphabet当中)。

2 先来看一眼两个标准中的the base64 alphabet(两个标准中的这个表是一样的):

                         Table 1: The Base 64 AlphabetValue Encoding  Value Encoding  Value Encoding  Value Encoding0 A            17 R            34 i            51 z1 B            18 S            35 j            52 02 C            19 T            36 k            53 13 D            20 U            37 l            54 24 E            21 V            38 m            55 35 F            22 W            39 n            56 46 G            23 X            40 o            57 57 H            24 Y            41 p            58 68 I            25 Z            42 q            59 79 J            26 a            43 r            60 810 K            27 b            44 s            61 911 L            28 c            45 t            62 +12 M            29 d            46 u            63 /13 N            30 e            47 v14 O            31 f            48 w         (pad) =15 P            32 g            49 x16 Q            33 h            50 y

并不包含换行符,这就可以解释为什么jdk8无法解码包含换行的编码结果

3 再来看一下jdk7中sun.misc.BASE64Encoder的类注释:

   This class implements a BASE64 Character encoder as specified in RFC1521. This RFC is part of the MIME specification as published by the Internet Engineering Task Force (IETF). Unlike some other encoding schemes there is nothing in this encoding that indicates where a buffer starts or ends. This means that the encoded text will simply start with the first line of encoded text and end with the last line of encoded text.

这个实现是按照RFC1521来的,类注释中并没有关于编码或者解码约束的说明。

4 那继续看一下rfc1521的关键部分(链接:https://tools.ietf.org/html/rfc1521)。

在5.2. Base64 Content-Transfer-Encoding章节有如下内容:

   The output stream (encoded bytes) must be represented in lines of nomore than 76 characters each.  All line breaks or other charactersnot found in Table 1 must be ignored by decoding software.  In base64data, characters other than those in Table 1, line breaks, and otherwhite space probably indicate a transmission error, about which awarning message or even a message rejection might be appropriateunder some circumstances.

这里明确规定了:

编码结果的每一行不能超过76个字符;
解码的字符必须在:Tbale 1(也就是之前提到的the base64 alphabet)、换行符和空白符这个范围内;
这就是为什么jdk7的编码结果包含换行。 
这样根据类注释和rfc协议内容,就可以解释上面通过测试代码得到的结论,也就可以理解为什么会产生这个问题。
 

‘sun’开头的包并不属于java规范,是sun公司的实现,所以jdk7中的这种base64编码方式并不是java的规范。

解决办法

那么,怎么解决这个问题呢: 
1. 使用apache common包中的org.apache.commons.codec.binary.Base64类进行编码和解码; 
2. 编码之后或解码之前去除换行符; 
3. 编码和解码使用相同的jdk版本;

其他Base64库

看看其他类库是怎么处理base64的。 
1. Apache Common

Apache Common中的org.apache.commons.codec.binary.Base64类是基于rfc2045实现的,根据类注释可以了解到此实现解码时忽略了所有不在the base64 alphabet范围内的字符,所以该实现可以处理包含换行符的base64编码结果。 
同时该类的编码方法提供了参数,用于指定编码结果长度在超过76个字符的时候是否添加换行,默认不换行。

Spring Core
Spring Core提供了Base64Utils类,该类只是一个工具类,并没有实现任何协议。

优先使用java8中的java.util.Base64类进行编码和解码;
如果java.util.Base64不存在,则会使用org.apache.commons.codec.binary.Base64;
如果都不存在,则会报错
协议简述

通过上面的排查步骤可以看到,rfc1521、rfc2045和rfc4648中关于base64的部分似乎不太一样,接下来分别简单看一下这三个协议是如何规范base64编码的换行的。

rfc1521(链接:https://tools.ietf.org/html/rfc1521) 
该协议是关于MIME的,Base64是MIME支持的一种编码类型。关键内容5.2. Base64 Content-Transfer-Encoding章节已经在上文中简单阐述过了,主要是规定了:编码结果每行长度和解码字符的范围。 
该协议已经被淘汰。 
jdk7基于该协议实现base64,所以编码结果会包含换行符。
 

MIME:Multipurpose Internet Mail Extensions,多用途互联网邮件扩展类型。是一个互联网标准,最早用于电子邮件系统,后来被应用到浏览器。服务器会将它们发送的多媒体数据的类型告诉浏览器,而通知手段就是说明该多媒体数据的MIME类型。

2.rfc2045(链接:https://tools.ietf.org/html/rfc2045)

该协议同样是关于MIME的,是rfc1521的更新版本,关键内容6.8. Base64 Content-Transfer-Encoding章节,其中关于编码结果长度和解码字符范围的规定与rfc1521并没有什么差别。

3.rfc4648

该协议是关于base16、base32和base64编码的。关于编码结果每行长度的说明在3.1. Line Feeds in Encoded Data章节:
 

   MIME is often used as a reference for base 64 encoding.  However,MIME does not define "base 64" per se, but rather a "base 64 Content-Transfer-Encoding" for use within MIME.  As such, MIME enforces alimit on line length of base 64-encoded data to 76 characters.  MIMEinherits the encoding from Privacy Enhanced Mail (PEM) [3], statingthat it is "virtually identical"; however, PEM uses a line length of64 characters.  The MIME and PEM limits are both due to limits withinSMTP.Implementations MUST NOT add line feeds to base-encoded data unlessthe specification referring to this document explicitly directs baseencoders to add line feeds after a specific number of characters.

大意是:

   MIME协议通常作为base64协议的引用。但是MIME协议并没有定义'base64',而是定义了'base64 内容传输编码'。因此MIME将base64编码的数据的长度限制为76个字符。...MIME和PEM关于长度的限制都是用于SMTP的。该协议的实现在编码结果中不能添加换行符,除非引用了该文档的实现中,明确说明在特定长度之后添加换行符。

jdk8的Base64类是基于rfc2045和rfc4648实现的,根据上文列出的协议内容可以确定,该类的编码结果不会包含换行符,而且在类的注释中明确说明了不会添加换行符。

以上!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/496460.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

争自动驾驶领头羊还是确保技术安全?欧美选择不同

来源&#xff1a;发掘新视界摘要&#xff1a;对于那些未知或有潜在危险的技术&#xff0c;欧洲更倾向于保护民众&#xff0c;而非是引领创新与进步。自优步自动驾驶汽车致人死亡事件发生之后&#xff0c;欧洲与美国对于技术的态度差异再度凸显&#xff0c;欧洲更倾向于加强监管…

菜鸟教程 之 JavaScript 教程

From&#xff1a;菜鸟教程 JavaScript&#xff1a;https://www.runoob.com/ W3School JavaScript 教程&#xff1a;http://www.w3school.com.cn/js/index.asp https://www.w3cschool.cn/javascript/ 廖雪峰官网 JavaScript 教程&#xff1a;https://www.liaoxuefeng.com/w…

关于Actionscript 3中给Flash传参数方法(一)

关于Actionscript 3中给Flash传参数方法&#xff08;一&#xff09; 今天在测试一个为Audi做Flash AD的时候&#xff0c;发现Audi提供的clicktag是Actionscript 2的代码&#xff0c;在Actionscript 3中是不被支持的&#xff0c;所以有去翻了Adobe Docs了。 在2.0中的clicktag代…

java byte num =1 3_java中把byte[]{1,2,3}通过怎样的转换,可以让其最终在TextView中显示为123...

展开全部string 转 byte[]byte[] midbytesisoString.getBytes("UTF8");//为UTF8编码byte[] isoret srt2.getBytes("ISO-8859-1");//为ISO-8859-1编码其中ISO-8859-1为单字节的编码2.byte[]转stringString isoString new String(bytes,"ISO-8859-1&q…

深度睡眠中,记忆如何被保持?

来源 &#xff1a;脑与智能丨公众号摘要&#xff1a;深度睡眠对记忆的巩固至关重要&#xff0c;与健康的睡眠相比&#xff0c;失眠会导致记忆力下降。深度睡眠中的慢波分为下降状态&#xff08;down state&#xff09;和上升状态&#xff08;up state&#xff09;&#xff0c;在…

itextPdf~将PDF页面大小转为A4格式

本文章向大家介绍将PDF页面大小转为A4格式---itextPdf实现&#xff0c;主要包括将PDF页面大小转为A4格式---itextPdf实现使用实例、应用技巧、基本知识点总结和需要注意事项&#xff0c;具有一定的参考价值&#xff0c;需要的朋友可以参考一下。 将PDF页面大小转为A4格式&…

java 数组 length 减少_java中数组有没有length()方法?string没有lenght()方法?

java中数组有没有length()方法&#xff0c;求数组的长度可以使用数组的length属性。int lengtharr.length;//求数组的长度----------------------------------------------------------------------------------------String 有length()方法&#xff0c;用来求字符串的长度int …

Oracle中临时表的深入研究

最近考虑到我们的数据库端写存储过程关于临时表使用的情况&#xff0c;由于我们现在还不清楚数据库端到底是怎么处理的&#xff0c;是否和Sql Server的处理方式相同&#xff0c;是否会存在隐患等等一些问题&#xff0c;为了避免将来不必要的麻烦我做了深入的研究和查看了一些权…

SQL 语句执行顺序

From&#xff1a;http://www.jellythink.com/archives/924 Oracle-SQL语句执行原理和完整过程详解&#xff1a;https://wenku.baidu.com/view/398bc427964bcf84b8d57b00.html 详解一条 SQL 语句的执行过程&#xff1a;http://www.cnblogs.com/cdf-opensource-007/p/6502556.…

无人车巨头每天都在做相同的事情:不惜血本做高精地图是为何?

来源&#xff1a;腾讯科技摘要&#xff1a;很多人都关注无人车的发展&#xff0c;殊不知无人车技术中&#xff0c;有十分重要的一环&#xff0c;是无人车的高精地图。高精地图成为无人车竞赛中的关键一环目前&#xff0c;很多家无人驾驶公司都在做着同样的事情&#xff1a;绘制…

itext实现HTML转换为PDF

1、首先&#xff0c;建立一个maven工程&#xff08;maven工程的建立这里不在演示&#xff09; 1.1 在maven工程的pom.xml中加入itext所需的jar包 <!-- itext 生成PDF jar--><dependency> <groupId>com.lowagie</groupId><artifactId>itext</…

phaser java_死磕 java同步系列之Phaser源码解析

问题(1)Phaser是什么&#xff1f;(2)Phaser具有哪些特性&#xff1f;(3)Phaser相对于CyclicBarrier和CountDownLatch的优势&#xff1f;简介Phaser&#xff0c;翻译为阶段&#xff0c;它适用于这样一种场景&#xff0c;一个大任务可以分为多个阶段完成&#xff0c;且每个阶段的…

闭包的应用

闭包的应用 闭包是ECMAScript最强大的特性之一。所谓“闭包”&#xff0c;是指在一个函数的执行过程中返回另一个函数对象。如&#xff1a;function testClosure(){ //private var a"private"; function getValue() { //引用testClosure的…

人工智能将为传统制造业带来什么?

来源 &#xff1a;数据科学与人工智能“物理世界”&#xff08;以制造业设备所代表&#xff09;和“数字世界”&#xff08;由人工智能、传感器等技术代表&#xff09;的碰撞催生了制造业的巨大的转变。两个世界的融合将为下一轮经济发展注入新的动能。以人工智能为代表的新技术…

解决JPA的枚举局限性

对于数据字典型字段&#xff0c;java的枚举比起Integer好处多多&#xff0c;比如 1、限定值&#xff0c;只能赋值枚举的那几个实例&#xff0c;不能像Integer随便输&#xff0c;保存和查询的时候特别有用 2、含义明确&#xff0c;使用时不需要去查数据字典 3、显示值跟存储值…

网络模拟器 eNSP、EVE-NG、GNS3、Packet Tracert

工欲善其事必先利其器&#xff0c;学习网络不可能都有真实的网络环境可以使用&#xff0c;这时就需要使用网络模拟软件&#xff0c;模拟一些网络环境&#xff0c;构建一些网络拓扑&#xff0c;然后学习研究网络技术 初学时不要在模拟器的选择上纠结&#xff0c;PT、GNS3 就足够…

linux 升级java_linux 升级jdk1.8

1、首先根据实际情况准备好包 jdk-8u45-linux-x64.rpm2、先不管是否已安装JDK1.6还是JDK1.7&#xff0c;先下载 jdk-8u45-linux-x64.rpm 然后上传到 /usr/local/src 去。当然其他目录也可以。这里是默认位置。3、给所有用户添加可执行权限#chmod x jdk-8u45-linux-x64.rpm都给…

第四讲 构建安全的Microsoft ASP.NET 应用的最佳实践和技术

*ASP.NET应用安全事项 *输入验证 1、参数化查询 2、存储过程 3、html编码(防止javascript脚本执行) String.Format("Invalid Logon for {0},please try again!",Server.HtmlEncode(UserName)); 认证与授权 配置管理 Review production configuration: <customErro…

信息技术守护人类文明DNA

来源&#xff1a;中国青年报 作者&#xff1a;胡春艳21世纪最时髦的技术&#xff0c;改变的不仅是未来&#xff0c;在被称为“最古老的研究”的文物与博物馆界&#xff0c;感受到的变化可能更加显著。天津大学文化遗产保护与传承信息技术研究中心主任张加万团队历时10年&…

如何给正面的负反馈

麦肯锡的一个方法论&#xff0c;就是如何给正面的负反馈。 如果你想给别人提意见的时候&#xff0c;用什么样的方法最有建设性&#xff1f; &#xff08;1&#xff09;我观察到你说了什么话&#xff0c;做了什么事&#xff1a;一、二、三、四…… &#xff08;2&#xff0…