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
沒有問題的話,還是采用這種方式吧
附件列表