package com.peanut.common.utils; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; import com.peanut.modules.common.dao.VodAesTokenDao; import com.peanut.modules.common.entity.VodAesTokenEntity; import lombok.Data; import java.util.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.util.Arrays; import java.util.List; @Component public class PlayToken { @Autowired private VodAesTokenDao vodAesTokenDao; //非AES生成方式,无需以下参数 private static String ENCRYPT_KEY = "abcdefgh#$&!mnopqrstuvwxyz123456"; //加密Key,为用户自定义的字符串,长度为16、24或32位 private static String INIT_VECTOR = "ABCDEFGHIJKLMNOP"; //加密偏移量,为用户自定义字符串,长度为16位,不能含有特殊字符 // public static void main(String[] args) throws Exception { // // String serviceId = "12"; // PlayToken playToken = new PlayToken(); // String aesToken = playToken.generateToken(serviceId); // //System.out.println("aesToken " + aesToken); // //System.out.println(playToken.validateToken(aesToken)); //验证解密部分 // // } /** * 根据传递的参数生成令牌 * 说明: * 1、参数可以是业务方的用户ID、播放终端类型等信息 * 2、调用令牌接口时生成令牌Token * @return */ public String generateToken() throws Exception { // if (null == args || args.length <= 0) { // return null; // } // String base = StringUtils.join(Arrays.asList(args), "_"); int bas = (int)(Math.random()*90+10); String base = String.valueOf(bas); //设置60S后,该token过期,过期时间可以自行调整 long expire = System.currentTimeMillis() + 60000L; base += "_" + expire; //自定义字符串,base的长度为16位字符(此例中,时间戳占13位,下划线(_)占1位,则还需传入2位字符。实际配置时也可按需全部更改,最终保证base为16、24或32位字符串即可。) //生成token String token = encrypt(base); //arg1为要加密的自定义字符串,arg2为加密Key //保存token,用于解密时校验token的有效性,例如:过期时间、token的使用次数 saveToken(token); return token; } /** * 验证token的有效性 * 说明: * 1、解密接口在返回播放密钥前,需要先校验Token的合法性和有效性 * 2、强烈建议同时校验Token的过期时间以及Token的有效使用次数 * @param token * @return * @throws Exception */ public boolean validateToken(String token) throws Exception { if (null == token || "".equals(token)) { return false; } String base = decrypt(token); //arg1为解密字符串,arg2为解密Key //先校验token的有效时间 Long expireTime = Long.valueOf(base.substring(base.lastIndexOf("_") + 1)); // System.out.println("时间校验:" + expireTime); if (System.currentTimeMillis() > expireTime) { return false; } //从DB获取token信息,判断token的有效性,业务方可自行实现 VodAesTokenEntity dbToken = getToken(token); //判断是否已经使用过该token if (dbToken == null || dbToken.getUseCount() > 0) { return false; } dbToken.setUseCount(1); vodAesTokenDao.updateById(dbToken); //获取到业务属性信息,用于校验 String businessInfo = base.substring(0, base.lastIndexOf("_")); String[] items = businessInfo.split("_"); //校验业务信息的合法性,业务方实现 return validateInfo(items); } /** * 保存Token到DB * 业务方自行实现 * * @param token */ public void saveToken(String token) { VodAesTokenEntity vodAesTokenEntity = new VodAesTokenEntity(); vodAesTokenEntity.setToken(token); vodAesTokenDao.insert(vodAesTokenEntity); //TODO 存储Token } /** * 查询Token * 业务方自行实现 * * @param token */ public VodAesTokenEntity getToken(String token) { List vodAesTokenEntities = vodAesTokenDao.selectList(new LambdaQueryWrapper().eq(VodAesTokenEntity::getToken, token)); if(vodAesTokenEntities.size()!=1){ return null; }else{ return vodAesTokenEntities.get(0); } } /** * 校验业务信息的有效性,业务方可自行实现 * * @param infos * @return */ private boolean validateInfo(String... infos) { //TODO 校验信息的有效性,例如UID是否有效等 return true; } public static String encrypt(String value) { try { IvParameterSpec iv = new IvParameterSpec(INIT_VECTOR.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(ENCRYPT_KEY.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); byte[] encrypted = cipher.doFinal(value.getBytes()); // 使用 URL 安全的 Base64 编码 return Base64.getUrlEncoder().encodeToString(encrypted); } catch (Exception ex) { ex.printStackTrace(); } return null; } public static String decrypt(String encrypted) { try { IvParameterSpec iv = new IvParameterSpec(INIT_VECTOR.getBytes("UTF-8")); SecretKeySpec skeySpec = new SecretKeySpec(ENCRYPT_KEY.getBytes("UTF-8"), "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); // 使用 URL 安全的 Base64 解码 byte[] original = cipher.doFinal(Base64.getUrlDecoder().decode(encrypted)); return new String(original); } catch (Exception ex) { ex.printStackTrace(); } return null; } /** * AES加密生成Token * * @param encryptStr 要加密的字符串 * @param encryptKey 加密Key * @return * @throws Exception */ // public String encrypt(String encryptStr, String encryptKey) throws Exception { // IvParameterSpec e = new IvParameterSpec(INIT_VECTOR.getBytes("UTF-8")); // SecretKeySpec skeySpec = new SecretKeySpec(encryptKey.getBytes("UTF-8"), "AES"); // Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); // cipher.init(Cipher.ENCRYPT_MODE, skeySpec, e); // byte[] encrypted = cipher.doFinal(encryptStr.getBytes()); // return Base64.encodeBase64String(encrypted); // } // // // /** // * AES解密token // * // * @param encryptStr 解密字符串 // * @param decryptKey 解密Key // * @return // * @throws Exception // */ // public String decrypt(String encryptStr, String decryptKey) throws Exception { // // IvParameterSpec e = new IvParameterSpec(INIT_VECTOR.getBytes("UTF-8")); // SecretKeySpec skeySpec = new SecretKeySpec(decryptKey.getBytes("UTF-8"), "AES"); // Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); // cipher.init(Cipher.DECRYPT_MODE, skeySpec, e); // // byte[] encryptByte = Base64.decodeBase64(encryptStr); // byte[] decryptByte = cipher.doFinal(encryptByte); // return new String(decryptByte); // } }