org.springframework.util.Base64Utils线程安全问题
Spring
提供的
org.springframework.util.Base64Utils
类,先会检测JDK里是否自带java.util.Base64,如果不带,则
使用的是
apache
提供的org.apache.commons.codec.binary.Base64:
经过一段时间的生产试运行来看, 在多线程环境中Base64Utils会出各种异常,比如这样的异常: java.lang.ArrayIndexOutOfBoundsException: null at org.apache.commons.codec.binary.BaseNCodec.readResults(BaseNCodec.java:209) at org.apache.commons.codec.binary.BaseNCodec.decode(BaseNCodec.java:324) at org.springframework.util.Base64Utils$CommonsCodecBase64Delegate.decode(Base64Utils.java:164) at org.springframework.util.Base64Utils.decodeFromString(Base64Utils.java:124) ... java.lang.NullPointerException: null at java.lang.System.arraycopy(Native Method) at org.apache.commons.codec.binary.BaseNCodec.readResults(BaseNCodec.java:209) at org.apache.commons.codec.binary.BaseNCodec.decode(BaseNCodec.java:324) at org.springframework.util.Base64Utils$CommonsCodecBase64Delegate.decode(Base64Utils.java:164) at org.springframework.util.Base64Utils.decodeFromString(Base64Utils.java:124) ... 又或者没抛异常,但Base64Utils.decodeFromString解出的数据已经不准确了,比如此时用它解码出的数据或秘钥来进行解密的话,会出类似如下异常: javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:750) at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676) at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java:294) at javax.crypto.Cipher.doFinal(Cipher.java:2087) ...
但具体由哪个类引起的并发,原因有待分析,可能与JDK版本、org.apache.commons.codec.binary.Base64、java.util.Base64都有关系 ,不管问题出在哪,在使用Base64Utils的地方,加上同步应该即可解决: synchronized (Base64Utils.class) { key = Base64Utils.decodeFromString(keyBase64); } synchronized (Base64Utils.class) { rtnValue = Base64Utils.encodeToString(data); } 或者不使用 org.springframework.util.Base64Utils,而是直接使用 apache 提供的org.apache.commons.codec.binary.Base64(相应方法:Base64.decodeBase64(keyBase64),Base64.encodeBase64String(data)) ,并发环境下目前发现没有问题,代码参见附件!!
由于 synchronized 同步会引起性能问题,如果直接使用org.apache.commons.codec.binary.Base64 没有问题的话,还是采用这种方式吧
扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄

经过一段时间的生产试运行来看, 在多线程环境中Base64Utils会出各种异常,比如这样的异常: java.lang.ArrayIndexOutOfBoundsException: null at org.apache.commons.codec.binary.BaseNCodec.readResults(BaseNCodec.java:209) at org.apache.commons.codec.binary.BaseNCodec.decode(BaseNCodec.java:324) at org.springframework.util.Base64Utils$CommonsCodecBase64Delegate.decode(Base64Utils.java:164) at org.springframework.util.Base64Utils.decodeFromString(Base64Utils.java:124) ... java.lang.NullPointerException: null at java.lang.System.arraycopy(Native Method) at org.apache.commons.codec.binary.BaseNCodec.readResults(BaseNCodec.java:209) at org.apache.commons.codec.binary.BaseNCodec.decode(BaseNCodec.java:324) at org.springframework.util.Base64Utils$CommonsCodecBase64Delegate.decode(Base64Utils.java:164) at org.springframework.util.Base64Utils.decodeFromString(Base64Utils.java:124) ... 又或者没抛异常,但Base64Utils.decodeFromString解出的数据已经不准确了,比如此时用它解码出的数据或秘钥来进行解密的话,会出类似如下异常: javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:750) at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676) at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java:294) at javax.crypto.Cipher.doFinal(Cipher.java:2087) ...
但具体由哪个类引起的并发,原因有待分析,可能与JDK版本、org.apache.commons.codec.binary.Base64、java.util.Base64都有关系 ,不管问题出在哪,在使用Base64Utils的地方,加上同步应该即可解决: synchronized (Base64Utils.class) { key = Base64Utils.decodeFromString(keyBase64); } synchronized (Base64Utils.class) { rtnValue = Base64Utils.encodeToString(data); } 或者不使用 org.springframework.util.Base64Utils,而是直接使用 apache 提供的org.apache.commons.codec.binary.Base64(相应方法:Base64.decodeBase64(keyBase64),Base64.encodeBase64String(data)) ,并发环境下目前发现没有问题,代码参见附件!!
由于 synchronized 同步会引起性能问题,如果直接使用org.apache.commons.codec.binary.Base64 没有问题的话,还是采用这种方式吧
附件列表
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

更多精彩