一、前端vue
-
控制台先加载AES
npm install crypto-js --save-dev
- 编写AES-CBC加密解密方法
import CryptoJS from "crypto-js"; /** * * 第一个参数word是待加密或者解密的字符串; * 第二个参数keyStr是aes加密需要用到的16位字符串的key; * 第三个参数是初始化向量 iv。 */ export default { // 加密 encrypt(word, keyStr, ivStr) { keyStr = keyStr ? keyStr : "16weizifuchuan16"; ivStr = ivStr ? ivStr : "1suibianshurude6"; let key = CryptoJS.enc.Utf8.parse(keyStr); let iv = CryptoJS.enc.Utf8.parse(ivStr); let srcs = CryptoJS.enc.Utf8.parse(word); let encrypted = CryptoJS.AES.encrypt(srcs, key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 //实测如果使用ZeroPadding后端会报bad padding错误 }); return encrypted.ciphertext.toString(); }, // 解密 decrypt(word, keyStr, ivStr) { keyStr = keyStr ? keyStr : "16weizifuchuan16"; ivStr = ivStr ? ivStr : "1suibianshurude6"; var key = CryptoJS.enc.Utf8.parse(keyStr); let iv = CryptoJS.enc.Utf8.parse(ivStr); var decrypt = CryptoJS.AES.decrypt(CryptoJS.format.Hex.parse(word), key, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); return CryptoJS.enc.Utf8.stringify(decrypt).toString(); } };
-
编写AES-ECB加密解密
import CryptoJS from "crypto-js"; /** * * 第一个参数word是待加密或者解密的字符串; * 第二个参数keyStr是aes加密需要用到的16位字符串的key; * * 如果想对一个js对象加密,需要先把该对象转成json字符串。 */ export default { // 加密 encrypt(word, keyStr) { keyStr = keyStr ? keyStr : "jingbablog201130"; let key = CryptoJS.enc.Utf8.parse(keyStr); let srcs = CryptoJS.enc.Utf8.parse(word); let encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); return encrypted.ciphertext.toString(); }, // 解密 decrypt(word, keyStr) { keyStr = keyStr ? keyStr : "jingbablog201130"; var key = CryptoJS.enc.Utf8.parse(keyStr); var decrypt = CryptoJS.AES.decrypt(CryptoJS.format.Hex.parse(word), key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }); return CryptoJS.enc.Utf8.stringify(decrypt).toString(); } };
-
编写base64参数
export function Base64() { var BASE64 = {enKey: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', deKey: new Array( -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1 ), encode: function(src){ //用一个数组来存放编码后的字符,效率比用字符串相加高很多。 var str=new Array(); var ch1, ch2, ch3; var pos=0; //每三个字符进行编码。 while(pos+3<=src.length){ ch1=src.charCodeAt(pos++); ch2=src.charCodeAt(pos++); ch3=src.charCodeAt(pos++); str.push(this.enKey.charAt(ch1>>2), this.enKey.charAt(((ch1<<4)+(ch2>>4))&0x3f)); str.push(this.enKey.charAt(((ch2<<2)+(ch3>>6))&0x3f), this.enKey.charAt(ch3&0x3f)); } //给剩下的字符进行编码。 if(pos<src.length){ ch1=src.charCodeAt(pos++); str.push(this.enKey.charAt(ch1>>2)); if(pos<src.length){ ch2=src.charCodeAt(pos); str.push(this.enKey.charAt(((ch1<<4)+(ch2>>4))&0x3f)); str.push(this.enKey.charAt(ch2<<2&0x3f), '='); }else{ str.push(this.enKey.charAt(ch1<<4&0x3f), '=='); } } //组合各编码后的字符,连成一个字符串。 return str.join(''); }, decode: function(src){ //用一个数组来存放解码后的字符。 var str=new Array(); var ch1, ch2, ch3, ch4; var pos=0; //过滤非法字符,并去掉'='。 // eslint-disable-next-line no-useless-escape src=src.replace(/[^A-Za-z0-9\+\/]/g, ''); //decode the source string in partition of per four characters. while(pos+4<=src.length){ ch1=this.deKey[src.charCodeAt(pos++)]; ch2=this.deKey[src.charCodeAt(pos++)]; ch3=this.deKey[src.charCodeAt(pos++)]; ch4=this.deKey[src.charCodeAt(pos++)]; str.push(String.fromCharCode( (ch1<<2&0xff)+(ch2>>4), (ch2<<4&0xff)+(ch3>>2), (ch3<<6&0xff)+ch4)); } //给剩下的字符进行解码。 if(pos+1<src.length){ ch1=this.deKey[src.charCodeAt(pos++)]; ch2=this.deKey[src.charCodeAt(pos++)]; if(pos<src.length){ ch3=this.deKey[src.charCodeAt(pos)]; str.push(String.fromCharCode((ch1<<2&0xff)+(ch2>>4), (ch2<<4&0xff)+(ch3>>2))); }else{ str.push(String.fromCharCode((ch1<<2&0xff)+(ch2>>4))); } } //组合各解码后的字符,连成一个字符串。 return str.join(''); } }; return BASE64; }
注:因为我的加密使用了两层,正常来讲最好是使用两个不同的加密方式尽心加密,但是本文只使用了AES的两种加密方式进行加密
-
简单使用
-
引入加密工具类
- 编写登陆按钮
- 测试
- 结果
6.但是我们不可能在每个接口都去写一遍加密的方法,以及解密的方法,所以我们需要进行统一的处理,所以我们需要在axios.js中编写请求处理方法
import axios from 'axios';
import First from "../public/thirdTool/AES_ECB.js";
import Second from "../public/thirdTool/AES_CBC.js";
import {Base64} from "../public/thirdTool/Base64";
/**
* 请求处理
*/
axios.interceptors.request.use(function (config) {
console.info("-------------------------------------");
console.info("加密前数据======config" + JSON.stringify(config));
//先加载一个uuid参数
// eslint-disable-next-line no-undef
let uuid = getUUID();
// eslint-disable-next-line no-useless-escape
uuid = uuid.replace(/\-/g, '');
let token = uuid;
if (token != null) {
config.headers.Authorization = 'Bearer ' + token;
}
let isEncryption = true;
if (config.url.indexOf("login/getSession") != -1) {
//加密数据并添加到请求中
config.headers.token = uuid;
} else {
if (isEncryption == true) {
config.headers.token = uuid;
} else {
config.headers.token = uuid;
}
}
if (isEncryption == false) {
return config;
}
let flag = false;
if (flag) {
return config;
}
let data = config.data;
if (config.headers['Content-Type'] == 'application/x-www-form-urlencoded;charset=UTF-8') {
var ret = "";
for (let it in data) {
ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
}
if (ret != "") {
ret = ret.substring(0, ret.length - 1);
data = ret.replace(/\+/g, " ");
let dataEntry = Second.encrypt(First.encrypt(Base64().encode(JSON.stringify(data))));
config.data = {
value: dataEntry
};
console.info();
}
} else {
if (data != null) {
data = JSON.stringify(data);
}
if (data && (typeof (data) == 'string')) {
let dataEntry = "value=" + Second.encrypt(First.encrypt(Base64().encode(data)));
config.headers['Content-Type'] = 'application/json';
config.data = dataEntry;
console.info();
}
}
console.info("加密后数据======config" + JSON.stringify(config));
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// eslint-disable-next-line no-unused-vars
function getUUID() {
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = "-";
var uuid = s.join("");
return uuid;
}
7.需要注意的是我们必须注意在加密的时候将参数先进行base64的加密,防止数据裸奔,并且AES后端解密也需要前端进行base64的加密
8.查看效果
到此为止,前端的加密方法已经写完了,之后就是写后端方法了
二、后端
- 在请求进来之后,我们需要添加过滤器,DeCodeFilter
/** * @author xiaoli * @create 2020-11-25 14:42:37 */ @WebFilter(urlPatterns = "/*") public class DecodeFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String uri = req.getRequestURI(); if(uri.contains("upload")){ chain.doFilter(request, response); //图像上传的请求,不做处理 }else{ String reqMethod = req.getMethod(); if("POST".equals(reqMethod)){ PrintWriter out = null; HttpServletResponse res = (HttpServletResponse) response; response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); WrapperedRequest requestWrapper = new WrapperedRequest(req); String body = requestWrapper.getBody(); //获取body数据 try { chain.doFilter(requestWrapper, response); } catch (Exception e) { e.printStackTrace(); } }else{ //get请求直接放行 chain.doFilter(request, response); } } } @Override public void destroy() { } }
- 在使用filter过滤器的时候我们需要在application启动项中添加扫描注解
- 添加WrapperedRequest参数处理类
package com.smile.jingbablog.filter; import com.smile.jingbablog.utils.AES_CBCUtils; import com.smile.jingbablog.utils.AES_ECBUtils; import com.smile.jingbablog.utils.Base64Decoder; import com.smile.jingbablog.utils.StrUtils; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.aspectj.util.FileUtil; import tk.mybatis.mapper.util.StringUtil; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; import java.net.URLDecoder; import java.nio.charset.Charset; import java.util.*; /** * @author xiaoli * @create 2020-11-25 14:54:40 */ public class WrapperedRequest extends HttpServletRequestWrapper { private static final int LOOPTIMES = 500*1024*1024;//限制为500M=500*1024Kb private static final int MAX_STR_LEN = Integer.MAX_VALUE; private String requestBody = ""; HttpServletRequest req = null; private Map<String, Object> parameterMap = new HashMap<String, Object>(); // 所有参数的Map集合 public WrapperedRequest(HttpServletRequest request) throws IOException { super(request); Enumeration e = request.getHeaderNames() ; while(e.hasMoreElements()){ String name = (String) e.nextElement(); String value = request.getHeader(name); // System.out.println(name+" = "+value); } String bodyString = HttpHelper.getBodyString(request); if(bodyString!=null&&bodyString.contains("=")){ bodyString = bodyString.split("=")[1]; } //先解析外层CBC String params = ""; try { String first = AES_CBCUtils.decrypt(bodyString); //AES_CBC解密方法 //解析内层ECB String second = null; second = AES_ECBUtils.decrypt(first); //AES_ECB解密方法 params = Base64Decoder.decode(second); try { JSONObject json = JSONObject.fromObject(params); try { Set<String> set = json.keySet(); for(String key :set) { parameterMap.put(key, json.getString(key)); } }catch (Exception export){ Set<String> set = json.keySet(); for(String key :set) { parameterMap.put(key, new String[]{json.getString(key)}); } } }catch(Exception ex) { String[] dataArr = params.split("&"); for(String str : dataArr) { String[] paramArra = str.split("="); parameterMap.put(paramArra[0], new String[]{URLDecoder.decode(paramArra.length>1?paramArra[1]:"", "UTF-8")}); } } //这一行数据可以不赋值给body,但是如果报Unrecognized token 'value': was expecting ('true', 'false' or 'null')这个错误的话,需要将body解密原controller才会接收到参数 this.requestBody = JSONObject.fromObject(parameterMap).toString(); } catch (Exception ex) { ex.printStackTrace(); } } private String readJSONString(HttpServletRequest request)throws UnsupportedEncodingException { try { request.setCharacterEncoding("UTF-8"); } catch (UnsupportedEncodingException e1) { throw new UnsupportedEncodingException("设置编码失败!"); } StringBuffer json = new StringBuffer(); String line = null; try { BufferedReader reader = request.getReader(); while ((line = readLine(reader)) != null) { json.append(line); } } catch (IOException e) { throw new UnsupportedEncodingException("编码转换失败!"); } return json.toString(); } /** * @Title: readLine * @Description: 读取一行 * @param @param br * @param @return * @param @throws IOException 参数说明 * @return String 返回类型 * @throws IOException */ public static String readLine( BufferedReader br) throws IOException{ StringBuffer sb = new StringBuffer(); int intC; int i=0; while ((intC = br.read()) != -1) { i++; if(i > LOOPTIMES) break; char c = (char) intC; if (c == '\n') { break; } if (sb.length() >= MAX_STR_LEN) { throw new RuntimeException("readLine input too long"); } sb.append(c); } if(sb.toString().length()>0){ return sb.toString().replace("\r", ""); }else if((char) intC == '\n' && i == 1){ return ""; }else{ return null; } } public String getBody() { return this.requestBody; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new StringReader(this.requestBody)); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream bais = new ByteArrayInputStream(this.requestBody.getBytes("UTF-8")); return new ServletInputStream() { @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener listener) { } @Override public int read() throws IOException { return bais.read(); } }; } @Override public String getHeader(String name) { return super.getHeader(name); } /** * 获取所有参数名 * @return 返回所有参数名 */ @Override public Enumeration<String> getParameterNames() { Vector<String> vector = new Vector<String>(parameterMap.keySet()); return vector.elements(); } /** * 获取指定参数名的值,如果有重复的参数名,则返回第一个的值 接收一般变量 ,如text类型 * @param name 指定参数名 * @return 指定参数名的值 */ @Override public String getParameter(String name) { String results = (String) parameterMap.get(name); if (StringUtils.isBlank(results)) return null; else { return results; } } /** * 获取指定参数名的所有值的数组,如:checkbox的所有数据 * 接收数组变量 ,如checkobx类型 */ @Override public String[] getParameterValues(String name) { String[] results = ((String) parameterMap.get(name)).split(","); if (results == null || results.length <= 0) return null; else { return results; } } }
- HttpHelper
/** * 用于将字节数组转换为字符串 * @author lijin * @create 2020-11-27 11:30:06 */ public class HttpHelper { public static String getBodyString(ServletRequest request) { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = request.getInputStream(); reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } }
- AES_CBCUtils
public class AES_CBCUtils { private static Logger logger = LoggerFactory.getLogger(AES_CBCUtils.class); /** * 默认算法 */ private static final String KEY = "16weizifuchuan16"; // 长度必须是 16 private static String iv = "1suibianshurude6";//偏移量字符串必须是16位 当模式是CBC的时候必须设置偏移量 private static String Algorithm = "AES"; private static String AlgorithmProvider = "AES/CBC/PKCS5Padding"; //算法/模式/补码方式 public static byte[] generatorKey() throws NoSuchAlgorithmException { KeyGenerator keyGenerator = KeyGenerator.getInstance(Algorithm); keyGenerator.init(256);//默认128,获得无政策权限后可为192或256 SecretKey secretKey = keyGenerator.generateKey(); return secretKey.getEncoded(); } public static IvParameterSpec getIv() throws UnsupportedEncodingException { IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes("utf-8")); System.out.println("偏移量:"+byteToHexString(ivParameterSpec.getIV())); return ivParameterSpec; } public static String encrypt(String src) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException { byte[] key = KEY.getBytes("utf-8"); SecretKey secretKey = new SecretKeySpec(key, Algorithm); IvParameterSpec ivParameterSpec = getIv(); Cipher cipher = Cipher.getInstance(AlgorithmProvider); cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec); byte[] cipherBytes = cipher.doFinal(src.getBytes(Charset.forName("utf-8"))); return byteToHexString(cipherBytes); } public static String decrypt(String src) throws Exception { byte[] key = KEY.getBytes("utf-8"); SecretKey secretKey = new SecretKeySpec(key, Algorithm); IvParameterSpec ivParameterSpec = getIv(); Cipher cipher = Cipher.getInstance(AlgorithmProvider); cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec); byte[] hexBytes = hexStringToBytes(src); byte[] plainBytes = cipher.doFinal(hexBytes); return new String(plainBytes); } /** * 将byte转换为16进制字符串 * @param src * @return */ public static String byteToHexString(byte[] src) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < src.length; i++) { int v = src[i] & 0xff; String hv = Integer.toHexString(v); if (hv.length() < 2) { sb.append("0"); } sb.append(hv); } return sb.toString(); } /** * 将16进制字符串装换为byte数组 * @param hexString * @return */ public static byte[] hexStringToBytes(String hexString) { hexString = hexString.toUpperCase(); int length = hexString.length() / 2; char[] hexChars = hexString.toCharArray(); byte[] b = new byte[length]; for (int i = 0; i < length; i++) { int pos = i * 2; b[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); } return b; } private static byte charToByte(char c) { return (byte) "0123456789ABCDEF".indexOf(c); } }
- AES_ECBUtils
public class AES_ECBUtils { private static Logger logger = LoggerFactory.getLogger(AES_ECBUtils.class); private static final String KEY = "jingbablog201130"; //key必须和前端的key相同 private static String Algorithm = "AES"; private static String AlgorithmProvider = "AES/ECB/PKCS5Padding"; // 算法/模式/补码方式 public static byte[] generatorKey() throws NoSuchAlgorithmException { KeyGenerator keyGenerator = KeyGenerator.getInstance(Algorithm); keyGenerator.init(256);//默认128,获得无政策权限后可为192或256 SecretKey secretKey = keyGenerator.generateKey(); return secretKey.getEncoded(); } public static String encrypt(String src) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException { byte[] key = KEY.getBytes("utf-8"); SecretKey secretKey = new SecretKeySpec(key, Algorithm); //IvParameterSpec ivParameterSpec = getIv(); Cipher cipher = Cipher.getInstance(AlgorithmProvider); cipher.init(Cipher.ENCRYPT_MODE, secretKey); byte[] cipherBytes = cipher.doFinal(src.getBytes(Charset.forName("utf-8"))); return byteToHexString(cipherBytes); } public static String decrypt(String src) throws Exception { byte[] key = KEY.getBytes("utf-8"); SecretKey secretKey = new SecretKeySpec(key, Algorithm); //IvParameterSpec ivParameterSpec = getIv(); Cipher cipher = Cipher.getInstance(AlgorithmProvider); cipher.init(Cipher.DECRYPT_MODE, secretKey); byte[] hexBytes = hexStringToBytes(src); byte[] plainBytes = cipher.doFinal(hexBytes); return new String(plainBytes); } /** * 将byte转换为16进制字符串 * @param src * @return */ public static String byteToHexString(byte[] src) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < src.length; i++) { int v = src[i] & 0xff; String hv = Integer.toHexString(v); if (hv.length() < 2) { sb.append("0"); } sb.append(hv); } return sb.toString(); } /** * 将16进制字符串装换为byte数组 * @param hexString * @return */ public static byte[] hexStringToBytes(String hexString) { hexString = hexString.toUpperCase(); int length = hexString.length() / 2; char[] hexChars = hexString.toCharArray(); byte[] b = new byte[length]; for (int i = 0; i < length; i++) { int pos = i * 2; b[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); } return b; } private static byte charToByte(char c) { return (byte) "0123456789ABCDEF".indexOf(c); } }
- BASE64Decode
package com.smile.jingbablog.utils; import java.io.*; public class Base64Decoder extends FilterInputStream { private static final char[] chars = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; // A mapping between char values and six-bit integers private static final int[] ints = new int[128]; static { for (int i = 0; i < 64; i++) { ints[chars[i]] = i; } } private int charCount; private int carryOver; /** * Creates a <code>FilterInputStream</code> * by assigning the argument <code>in</code> * to the field <code>this.in</code> so as * to remember it for later use. * * @param in the underlying input stream, or <code>null</code> if * this instance is to be created without an underlying stream. */ protected Base64Decoder(InputStream in) { super(in); } /*** * Returns the next decoded character from the stream, or -1 if end of * stream was reached. * * @return the decoded character, or -1 if the end of the input stream is * reached * @exception IOException * if an I/O error occurs */ public int read() throws IOException { // Read the next non-whitespace character int x; do { x = in.read(); if (x == -1) { return -1; } } while (Character.isWhitespace((char) x)); charCount++; // The '=' sign is just padding if (x == '=') { return -1; // effective end of stream } // Convert from raw form to 6-bit form x = ints[x]; // Calculate which character we're decoding now int mode = (charCount - 1) % 4; // First char save all six bits, go for another if (mode == 0) { carryOver = x & 63; return read(); } // Second char use previous six bits and first two new bits, // save last four bits else if (mode == 1) { int decoded = ((carryOver << 2) + (x >> 4)) & 255; carryOver = x & 15; return decoded; } // Third char use previous four bits and first four new bits, // save last two bits else if (mode == 2) { int decoded = ((carryOver << 4) + (x >> 2)) & 255; carryOver = x & 3; return decoded; } // Fourth char use previous two bits and all six new bits else if (mode == 3) { int decoded = ((carryOver << 6) + x) & 255; return decoded; } return -1; // can't actually reach this line } /*** * Reads decoded data into an array of bytes and returns the actual number * of bytes read, or -1 if end of stream was reached. * * @param buf * the buffer into which the data is read * @param off * the start offset of the data * @param len * the maximum number of bytes to read * @return the actual number of bytes read, or -1 if the end of the input * stream is reached * @exception IOException * if an I/O error occurs */ public int read(byte[] buf, int off, int len) throws IOException { if (buf.length < (len + off - 1)) { throw new IOException("The input buffer is too small: " + len + " bytes requested starting at offset " + off + " while the buffer " + " is only " + buf.length + " bytes long."); } // This could of course be optimized int i; for (i = 0; i < len; i++) { int x = read(); if (x == -1 && i == 0) { // an immediate -1 returns -1 return -1; } else if (x == -1) { // a later -1 returns the chars read so far break; } buf[off + i] = (byte) x; } return i; } /*** * Returns the decoded form of the given encoded string, as a String. Note * that not all binary data can be represented as a String, so this method * should only be used for encoded String data. Use decodeToBytes() * otherwise. * * @param encoded * the string to decode * @return the decoded form of the encoded string */ public static String decode(String encoded) { return new String(decodeToBytes(encoded)); } /*** * Returns the decoded form of the given encoded string, as bytes. * * @param encoded * the string to decode * @return the decoded form of the encoded string */ public static byte[] decodeToBytes(String encoded) { byte[] bytes = null; try { bytes = encoded.getBytes("UTF-8"); } catch (UnsupportedEncodingException ignored) { } Base64Decoder in = new Base64Decoder(new ByteArrayInputStream(bytes)); ByteArrayOutputStream out = new ByteArrayOutputStream( (int) (bytes.length * 0.67)); try { byte[] buf = new byte[4 * 1024]; // 4K buffer int bytesRead; while ((bytesRead = in.read(buf)) != -1) { out.write(buf, 0, bytesRead); } out.close(); return out.toByteArray(); } catch (IOException ignored) { return null; } } }
-
测试效果