JWT 加密
跟随上一篇的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; } }
由于这个加密文件没有经过我手去写,我顺便加了注释进去,希望对大家能有帮助,可以得话记得添加一下关注,谢谢!