跟隨上一篇的Gson序列化器方法的重寫,涉及到了JWT的加密;
首先先簡單介紹一下JWT:JWT是json web token縮寫。它將用戶信息加密到token里,服務器不保存任何用戶信息。服務器通過使用保存的密鑰驗證token的正確性,只要正確即通過驗證。
優點是在分布式系統中,很好地解決了單點登錄問題,很容易解決了session共享的問題。
缺點是無法作廢已頒布的令牌/不易應對數據過期。
這里我們使用JWT對文件路徑進行了加密,token失效時間為3分鍾,接下來直接看代碼吧:
首先POM文件里面引入依賴
1 <dependency> 2 <groupId>com.auth0</groupId> 3 <artifactId>java-jwt</artifactId> 4 <version>3.3.0</version> 5 </dependency>
然后編輯自己的加密工具類:CommonEnctyptUtil.java
import com.alibaba.fastjson.JSONObject; import com.apollo.alds.util.ConvertionUtil; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; import org.apache.log4j.Logger; import java.io.UnsupportedEncodingException; import java.util.Calendar; import java.util.HashMap; import java.util.Map; /** * @Auther: SHIYIXIN * @Date: 2018/11/26 16:10 * @Description: 文件名稱加密工具類 */ public class CommonEnctyptUtil { private static final Logger LOG = Logger.getLogger(CommonEnctyptUtil.class); private static final String KEY="DF94CBDCA294DC5DEF1368E64313FD3B98FE5EBCAB7F23AE"; private static String getMessage(int code) { switch (code) { case 1: return "解密失敗!"; case 2: return "token為空無效"; case 3: return "token無效或已過期"; default: return "其他未知錯誤"; } } /** * 文件加密 * @param fileName 文件名 * @param minutes token 過期時間 * @return 加密后文件名 */ public static String getAESResult(String fileName,int minutes){ String result=""; JSONObject json=new JSONObject(); json.put("fileName",fileName); String token=""; try { token=getToken(fileName,minutes); System.out.println("token="+token); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } if(!"".equals(token)){ json.put("token",token); } result= AESUtil.encrypt(KEY, json.toJSONString()); LOG.info("getAESResult="+result); return result; } /** * * @param fileName 文件名 * @param minutes 過期時間 * @return * @throws UnsupportedEncodingException */ public static String getToken( String fileName,int minutes) throws UnsupportedEncodingException { LOG.info("fileName:" + fileName); LOG.info("minutes:" + minutes); final Calendar instance = Calendar.getInstance(); instance.add(Calendar.MINUTE, minutes); Algorithm algorithm = Algorithm.HMAC256("secret"); String token = JWT.create().withClaim("fileName", fileName).withIssuer("auth0") .withExpiresAt(instance.getTime()).sign(algorithm); LOG.info("getToken="+token); return token; } /** * 密文解密 * @param info 加密字符串 * @return */ public static Map getDecryptInfo(String info){ Map result=new HashMap(); boolean res=false; int code=0; String msg=""; LOG.info("getDecryptInfo-----"+info); String message=AESUtil.decrypt(KEY, info);//解密 LOG.info("解密后信息:{}"+message); if(message!=null&&!"".equals(message)){ JSONObject json=JSONObject.parseObject(message); String token= ConvertionUtil.getSimpleStringWithNull(json.get("token")); String fileName=ConvertionUtil.getSimpleStringWithNull(json.get("fileName")); if(!"".equals(token)){ if(checktoken(fileName,token)){ res=true; msg=fileName; }else{ code=3; msg=getMessage(code); } }else{ code=2; msg=getMessage(code); } }else{ code=1; msg=getMessage(code); } result.put("result",res); result.put("code",code); result.put("msg",msg); LOG.info("getDecryptResult-----"+result); return result; } public static boolean checktoken(String fileName,String token) { LOG.info("fileName:" + fileName); boolean result=false; try { Algorithm algorithm = Algorithm.HMAC256("secret"); JWTVerifier verifier = JWT.require(algorithm).withIssuer("auth0").withClaim("fileName", fileName).build(); // Reusable verifier instance // 這里需注意的是,依賴的fasterxml.jackson.core 的版本必須是2.9.2以上的 DecodedJWT jwt = verifier.verify(token); result=true; } catch (JWTVerificationException ex) { // log LOG.info("authcheckfailed:", ex); } catch (UnsupportedEncodingException e) { LOG.info("編碼錯誤:", e); } catch (Exception e){ LOG.info("checktokenerror",e); } return result; } }
AESUtil類:
import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.security.SecureRandom; /** * @Auther: shiyixin * @Date: 2018/11/26 15:45 * @Description: */ public class AESUtil { private static final Logger logger = Logger.getLogger(AESUtil.class); private static final String defaultCharset = "UTF-8"; private static final String KEY_AES = "AES"; private static final String KEY = "123456"; /** * 加密 * * @param data 需要加密的內容 * @param key 加密密碼 * @return */ public static String encrypt(String key,String data ) { return doAES(data, key, Cipher.ENCRYPT_MODE); } /** * 解密 * * @param data 待解密內容 * @param key 解密密鑰 * @return */ public static String decrypt(String key,String data ) { return doAES(data, key, Cipher.DECRYPT_MODE); } /** * 加解密 * * @param data 待處理數據 * @param * @param mode 加解密mode * @return */ private static String doAES(String data, String key, int mode) { try { if (StringUtils.isBlank(data) || StringUtils.isBlank(key)) { return null; } //判斷是加密還是解密 boolean encrypt = mode == Cipher.ENCRYPT_MODE; byte[] content; //true 加密內容 false 解密內容 if (encrypt) { content = data.getBytes(defaultCharset); } else { content = parseHexStr2Byte(data); } //1.構造密鑰生成器,指定為AES算法,不區分大小寫 KeyGenerator kgen = KeyGenerator.getInstance(KEY_AES); SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); random.setSeed(key.getBytes()); //2.根據ecnodeRules規則初始化密鑰生成器 //生成一個128位的隨機源,根據傳入的字節數組 kgen.init(128, random); //3.產生原始對稱密鑰 SecretKey secretKey = kgen.generateKey(); //4.獲得原始對稱密鑰的字節數組 byte[] enCodeFormat = secretKey.getEncoded(); //5.根據字節數組生成AES密鑰 SecretKeySpec keySpec = new SecretKeySpec(enCodeFormat, KEY_AES); //6.根據指定算法AES自成密碼器 Cipher cipher = Cipher.getInstance(KEY_AES);// 創建密碼器 //7.初始化密碼器,第一個參數為加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二個參數為使用的KEY cipher.init(mode, keySpec);// 初始化 byte[] result = cipher.doFinal(content); if (encrypt) { //將二進制轉換成16進制 return parseByte2HexStr(result); } else { return new String(result, defaultCharset); } } catch (Exception e) { } return null; } /** * 將二進制轉換成16進制 * * @param buf * @return */ public static String parseByte2HexStr(byte buf[]) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < buf.length; i++) { String hex = Integer.toHexString(buf[i] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } sb.append(hex.toUpperCase()); } return sb.toString(); } /** * 將16進制轉換為二進制 * * @param hexStr * @return */ public static byte[] parseHexStr2Byte(String hexStr) { if (hexStr.length() < 1) { return null; } byte[] result =null; try { result= new byte[hexStr.length() / 2]; for (int i = 0; i < hexStr.length() / 2; i++) { int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16); int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16); result[i] = (byte) (high * 16 + low); } }catch(Exception e){ logger.info("parseHexStr2Byte密文處理異常"); } return result; } }
由於這個加密文件沒有經過我手去寫,我順便加了注釋進去,希望對大家能有幫助,可以得話記得添加一下關注,謝謝!