Java中Base64解码非法字符2d(-)的解决方案

0 次阅读

在Java项目开发过程中,Base64编码和解码经常用于处理文件传输、接口参数加密、Token生成以及数据存储等场景。但不少开发者在使用Base64解码时,会遇到类似“非法字符2d(-)”的异常:

java.lang.IllegalArgumentException: Illegal base64 character 2d

其中的“2d”代表十六进制字符编码,对应ASCII字符中的短横线“-”。这个错误说明当前使用的Base64解码器无法识别输入字符串中的“-”字符。

实际上,问题通常并不是数据损坏,而是编码格式选择不匹配。理解普通Base64和URL安全Base64之间的区别,可以快速解决这一类异常。

Base64非法字符2d(-)产生的原因

标准Base64编码规则使用64个字符表示二进制数据:

A-Z
a-z
0-9
+
/

其中:

  • “+”表示第62个字符;

  • “/”表示第63个字符;

  • “=”用于补位。

但是,在URL传输环境中,“+”和“/”可能会被特殊处理。例如:

  • URL参数中“+”可能被解析为空格;

  • “/”可能影响路径结构。

因此,Base64又衍生出了URL安全版本(Base64 URL Safe)。

URL安全Base64会替换部分字符:

标准Base64URL安全Base64
+-
/_

所以:

普通Base64:
a+b/cd==

URL安全Base64:
a-b_cd==

当一个经过URL安全Base64编码的数据,使用普通Base64解码器处理时,就会出现:

Illegal base64 character 2d

因为普通Base64解码器不认识“-”。

Java中常见错误写法

很多情况下,代码类似下面这样:

import java.util.Base64;

public class DecodeTest {
    public static void main(String[] args) {
        String value = "eyJhbGciOiJIUzI1NiJ9-test";

        byte[] result = Base64.getDecoder().decode(value);

        System.out.println(new String(result));
    }
}

执行后可能出现:

java.lang.IllegalArgumentException:
Illegal base64 character 2d

原因是:

Base64.getDecoder()

使用的是标准Base64解码规则,只支持:

+
/

不支持:

-
_

解决方案一:使用URL安全Base64解码器

如果数据来源是JWT、URL参数或者Web接口传输内容,通常应该使用URL安全Base64解码方式。

Java提供了对应的方法:

Base64.getUrlDecoder()

示例:

import java.util.Base64;

public class Base64DecodeDemo {

    public static void main(String[] args) {

        String data = "eyJhbGciOiJIUzI1NiJ9-test";

        byte[] bytes = Base64.getUrlDecoder().decode(data);

        String result = new String(bytes);

        System.out.println(result);
    }
}

相比:

Base64.getDecoder()

URL解码器能够正确识别:

-
_

因此可以避免非法字符2d问题。

解决方案二:替换字符后使用普通Base64解码

如果项目中已经固定使用:

Base64.getDecoder()

也可以在解码之前手动转换:

String data = "abc-def_123";

data = data.replace('-', '+')
           .replace('_', '/');

byte[] result = Base64.getDecoder().decode(data);

这种方式本质上是将URL安全Base64转换成标准Base64。

不过需要注意,这种方法适合兼容旧系统,不建议作为新项目的主要方案。

解决方案三:处理Base64补位问题

除了非法字符2d之外,URL安全Base64还可能缺少:

=

补位字符。

例如:

标准Base64:

YWJjZA==

URL安全Base64可能变成:

YWJjZA

部分Java版本在解码时要求完整格式,因此可以手动补齐:

public static String addPadding(String value) {

    int mod = value.length() % 4;

    if (mod > 0) {
        value += "====".substring(mod);
    }

    return value;
}

完整处理:

String data = addPadding(input);

byte[] result = Base64.getUrlDecoder().decode(data);

Base64解码在JWT中的典型场景

JWT(JSON Web Token)大量使用URL安全Base64编码。

一个JWT通常格式如下:

header.payload.signature

例如:

eyJhbGciOiJIUzI1NiJ9.
eyJ1c2VyIjoiYWRtaW4ifQ.
abc-def_xyz

其中:

  • Header使用Base64URL编码;

  • Payload使用Base64URL编码;

  • Signature同样采用Base64URL形式。

如果直接使用:

Base64.getDecoder()

解析JWT内容,很容易遇到:

Illegal base64 character 2d

正确方式:

Base64.getUrlDecoder()

例如解析Payload:

String payload = jwt.split("\.")[1];

String json = new String(
    Base64.getUrlDecoder().decode(payload)
);

System.out.println(json);

如何判断Base64字符串属于哪种格式

可以通过字符判断:

普通Base64:

包含 + 或 /

URL安全Base64:

包含 - 或 _

例如:

普通:

YWJjK2RlZg==

URL安全:

YWJjLWRlZg

如果字符串来自:

  • 浏览器URL参数;

  • JWT Token;

  • OAuth授权信息;

  • REST接口请求;

大概率应该使用:

Base64.getUrlDecoder()

Apache Commons Codec中的处理方式

如果项目使用Apache Commons Codec,也需要选择正确方法。

标准Base64:

Base64.decodeBase64(data);

URL安全Base64:

Base64.decodeBase64URLSafe(data);

或者:

Base64 base64 = new Base64(true);

byte[] result = base64.decode(data);

其中:

true

表示启用URL安全模式。

常见错误总结

错误一:混用编码和解码方式

错误:

Base64.getEncoder()

生成:

abc-def

然后:

Base64.getDecoder()

解析。

解决:

编码和解码必须保持一致:

Base64.getUrlEncoder()
Base64.getUrlDecoder()

配套使用。


错误二:误认为“-”导致数据异常

很多开发者看到:

Illegal base64 character 2d

会认为字符串被破坏。

实际上:

2d = '-'

只是编码标准不同。


错误三:直接复制JWT内容解析

JWT中的:

header.payload.signature

不是完整Base64字符串。

不能直接:

decode(jwt)

应该拆分:

String[] parts = jwt.split("\.");

分别解析对应部分。

最佳实践建议

为了避免Java Base64解码非法字符2d问题,可以遵循以下原则:

  1. 明确数据来源使用的是Base64还是Base64URL。

  2. URL、Cookie、JWT场景优先使用:

Base64.getUrlEncoder()
Base64.getUrlDecoder()
  1. 文件存储、内部数据交换可以使用:

Base64.getEncoder()