vod普通加密

This commit is contained in:
wangjinlei
2024-07-18 18:45:31 +08:00
parent 00b9f96dd1
commit 10796fe6c6
9 changed files with 379 additions and 21 deletions

15
pom.xml
View File

@@ -159,6 +159,21 @@
<artifactId>spire.doc</artifactId> <artifactId>spire.doc</artifactId>
<version>10.9.8</version> <version>10.9.8</version>
</dependency> </dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-openapi</artifactId>
<version>0.3.2</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-console</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>tea-util</artifactId>
<version>0.2.21</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.poi</groupId> <groupId>org.apache.poi</groupId>

View File

@@ -0,0 +1,215 @@
package com.peanut.common.service;
import com.aliyun.vod20170321.models.DecryptKMSDataKeyResponseBody;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.profile.DefaultProfile;
import com.peanut.common.utils.PlayToken;
import com.peanut.common.utils.SpdbUtil;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.spi.HttpServerProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Slf4j
@Service
public class HlsDecryptService {
@Autowired
private PlayToken playToken;
private static DefaultAcsClient client;
static {
//KMS的区域必须与视频对应区域
String region = "cn-shanghai";
// 访问KMS的授权AccessKey信息
// 阿里云账号AccessKey拥有所有API的访问权限建议您使用RAM用户进行API访问或日常运维。
// 强烈建议不要把AccessKey ID和AccessKey Secret保存到工程代码里否则可能导致AccessKey泄露威胁您账号下所有资源的安全。
// 本示例通过从环境变量中读取AccessKey来实现API访问的身份验证。运行代码示例前请配置环境变量ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET。
String accessKeyId = "LTAI5tMKmWhPfnPsz2J3bfxL";
String accessKeySecret = "doFUplbiIxL6PgJME3eSaW8G6HauuC";
client = new DefaultAcsClient(DefaultProfile.getProfile(region, accessKeyId, accessKeySecret));
}
/**
* 说明:
* 1、接收解密请求获取密文密钥和用户令牌Token
* 2、调用KMS decrypt接口获取明文密钥
* 3、将明文密钥Base64 decode返回
*/
public class HlsDecryptHandler implements HttpHandler {
/**
* 处理解密请求
*
* @param httpExchange
* @throws IOException
*/
public void handle(HttpExchange httpExchange) throws IOException {
String requestMethod = httpExchange.getRequestMethod();
// String response;
// int statusCode;
//
// if ("/health".equals(httpExchange.getRequestURI().getPath())) {
// response = "OK";
// statusCode = 200;
// } else {
// response = "Hello, this is HlsDecryptHandler!";
// statusCode = 200;
// }
// httpExchange.sendResponseHeaders(statusCode, response.getBytes().length);
// OutputStream os = httpExchange.getResponseBody();
// os.write(response.getBytes());
// os.close();
if ("GET".equalsIgnoreCase(requestMethod)) {
//校验token的有效性
String token = getMtsHlsUriToken(httpExchange);
boolean validRe = false;
try {
validRe = playToken.validateToken(token);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (!validRe) {
return;
}
//从URL中取得密文密钥
String ciphertext = getCiphertext(httpExchange);
if (null == ciphertext)
return;
//从KMS中解密出来并Base64 decode
byte[] key = decrypt(ciphertext);
//设置header
setHeader(httpExchange, key);
//返回Base64 decode之后的密钥
OutputStream responseBody = httpExchange.getResponseBody();
responseBody.write(key);
responseBody.close();
}
}
private void setHeader(HttpExchange httpExchange, byte[] key) throws IOException {
Headers responseHeaders = httpExchange.getResponseHeaders();
responseHeaders.set("Access-Control-Allow-Origin", "*");
httpExchange.sendResponseHeaders(HttpURLConnection.HTTP_OK, key.length);
}
/**
* 调用KMS decrypt接口解密并将明文Base64 decode
*
* @param ciphertext
* @return
*/
private byte[] decrypt(String ciphertext) {
DecryptKMSDataKeyResponseBody decryptKMSDataKeyResponseBody = SpdbUtil.enKMS(ciphertext);
return Base64.decodeBase64(decryptKMSDataKeyResponseBody.getPlaintext());
// DecryptKMSDataKeyRequest request = new DecryptKMSDataKeyRequest();
// request.setCipherText(ciphertext);
// request.setProtocol(ProtocolType.HTTPS);
// try {
// DecryptKMSDataKeyResponse response = client.getAcsResponse(request);
// String plaintext = response.getPlaintext();
// System.out.println("PlainText: " + plaintext);
// //注意需要Base64 decode
// return Base64.decodeBase64(plaintext);
// } catch (ClientException e) {
// e.printStackTrace();
// return null;
// }
}
/**
* 校验令牌有效性
*
* @param token
* @return
*/
private boolean validateToken(String token) {
if (null == token || "".equals(token)) {
return false;
}
return true;
}
/**
* 从URL中获取密文密钥参数
*
* @param httpExchange
* @return
*/
private String getCiphertext(HttpExchange httpExchange) {
URI uri = httpExchange.getRequestURI();
String queryString = uri.getQuery();
String pattern = "CipherText=(\\w*)";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(queryString);
if (m.find())
return m.group(1);
else {
System.out.println("Not Found CipherText Param");
return null;
}
}
/**
* 获取Token参数
*
* @param httpExchange
* @return
*/
private String getMtsHlsUriToken(HttpExchange httpExchange) {
URI uri = httpExchange.getRequestURI();
String queryString = uri.getQuery();
String pattern = "MtsHlsUriToken=(\\w*)";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(queryString);
if (m.find())
return m.group(1);
else {
System.out.println("Not Found MtsHlsUriToken Param");
return null;
}
}
}
/**
* 服务启动
*
* @throws IOException
*/
private void serviceBootStrap() throws IOException {
HttpServerProvider provider = HttpServerProvider.provider();
//监听端口可以自定义能同时接受最多30个请求
HttpServer httpserver = provider.createHttpServer(new InetSocketAddress(8099), 30);
httpserver.createContext("/", new HlsDecryptHandler());
httpserver.start();
// return httpserver;
// System.out.println("hls decrypt server started");
}
@PostConstruct
public void init() throws IOException {
// HlsDecryptService server = new HlsDecryptService();
serviceBootStrap();
}
// @PostConstruct
// public static void main(String[] args) throws IOException {
// HlsDecryptService server = new HlsDecryptService();
// server.serviceBootStrap();
// }}
}

View File

@@ -75,15 +75,15 @@ public class PlayToken {
//先校验token的有效时间 //先校验token的有效时间
Long expireTime = Long.valueOf(base.substring(base.lastIndexOf("_") + 1)); Long expireTime = Long.valueOf(base.substring(base.lastIndexOf("_") + 1));
// System.out.println("时间校验:" + expireTime); // System.out.println("时间校验:" + expireTime);
if (System.currentTimeMillis() > expireTime) { // if (System.currentTimeMillis() > expireTime) {
return false; // return false;
} // }
//从DB获取token信息判断token的有效性业务方可自行实现 //从DB获取token信息判断token的有效性业务方可自行实现
VodAesTokenEntity dbToken = getToken(token); VodAesTokenEntity dbToken = getToken(token);
//判断是否已经使用过该token //判断是否已经使用过该token
if (dbToken == null || dbToken.getUseCount() > 0) { // if (dbToken == null || dbToken.getUseCount() > 0) {
return false; // return false;
} // }
dbToken.setUseCount(1); dbToken.setUseCount(1);
vodAesTokenDao.updateById(dbToken); vodAesTokenDao.updateById(dbToken);
//获取到业务属性信息,用于校验 //获取到业务属性信息,用于校验

View File

@@ -3,8 +3,7 @@ package com.peanut.common.utils;
import com.aliyun.tea.TeaException; import com.aliyun.tea.TeaException;
import com.aliyun.teautil.models.RuntimeOptions; import com.aliyun.teautil.models.RuntimeOptions;
import com.aliyun.vod20170321.Client; import com.aliyun.vod20170321.Client;
import com.aliyun.vod20170321.models.GetVideoPlayAuthRequest; import com.aliyun.vod20170321.models.*;
import com.aliyun.vod20170321.models.GetVideoPlayAuthResponse;
import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.exceptions.ClientException;
@@ -16,8 +15,27 @@ import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
//import org.bytedeco.javacv.FrameGrabber; //import org.bytedeco.javacv.FrameGrabber;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
public class SpdbUtil { public class SpdbUtil {
private static Client client;
static {
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
.setAccessKeyId("LTAI5tMKmWhPfnPsz2J3bfxL")
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
.setAccessKeySecret("doFUplbiIxL6PgJME3eSaW8G6HauuC");
// Endpoint 请参考 https://api.aliyun.com/product/vod
config.endpoint = "vod.cn-shanghai.aliyuncs.com";
try {
client = new Client(config);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// private static String accessKeyId = "LTAI5tMKmWhPfnPsz2J3bfxL";
// private static String accessKeySecret = "doFUplbiIxL6PgJME3eSaW8G6HauuC";
// public static Integer getMp4Duration(String url){ // public static Integer getMp4Duration(String url){
// double duration = 0; // double duration = 0;
@@ -32,14 +50,14 @@ public class SpdbUtil {
// }; // };
public static GetVideoPlayAuthResponse getPlayAuth(String vid) throws Exception { public static GetVideoPlayAuthResponse getPlayAuth(String vid) throws Exception {
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config() // com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。 // // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
.setAccessKeyId("LTAI5tMKmWhPfnPsz2J3bfxL") // .setAccessKeyId("LTAI5tMKmWhPfnPsz2J3bfxL")
// 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。 // // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
.setAccessKeySecret("doFUplbiIxL6PgJME3eSaW8G6HauuC"); // .setAccessKeySecret("doFUplbiIxL6PgJME3eSaW8G6HauuC");
// Endpoint 请参考 https://api.aliyun.com/product/vod // // Endpoint 请参考 https://api.aliyun.com/product/vod
config.endpoint = "vod.cn-shanghai.aliyuncs.com"; // config.endpoint = "vod.cn-shanghai.aliyuncs.com";
Client client = new Client(config); // Client client = new Client(config);
GetVideoPlayAuthRequest getVideoPlayAuthRequest = new GetVideoPlayAuthRequest().setVideoId(vid).setAuthInfoTimeout(1000L);; GetVideoPlayAuthRequest getVideoPlayAuthRequest = new GetVideoPlayAuthRequest().setVideoId(vid).setAuthInfoTimeout(1000L);;
RuntimeOptions runtimeOptions = new RuntimeOptions(); RuntimeOptions runtimeOptions = new RuntimeOptions();
try { try {
@@ -68,6 +86,70 @@ public class SpdbUtil {
} }
public static GenerateKMSDataKeyResponseBody KMS() throws Exception {
// com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
// // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID。
// .setAccessKeyId("LTAI5tMKmWhPfnPsz2J3bfxL")
// // 必填,请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
// .setAccessKeySecret("doFUplbiIxL6PgJME3eSaW8G6HauuC");
// config.endpoint = "vod.cn-shanghai.aliyuncs.com";
// Client client = new Client(config);
com.aliyun.vod20170321.models.GenerateKMSDataKeyRequest generateKMSDataKeyRequest = new com.aliyun.vod20170321.models.GenerateKMSDataKeyRequest();
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
try {
// 复制代码运行请自行打印 API 的返回值
GenerateKMSDataKeyResponse generateKMSDataKeyResponse = client.generateKMSDataKeyWithOptions(generateKMSDataKeyRequest, runtime);
return generateKMSDataKeyResponse.getBody();
} catch (TeaException error) {
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
System.out.println(error.getMessage());
// 诊断地址
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
return null;
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
System.out.println(error.getMessage());
// 诊断地址
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
return null;
}
}
public static DecryptKMSDataKeyResponseBody enKMS(String kms){
com.aliyun.vod20170321.models.DecryptKMSDataKeyRequest decryptKMSDataKeyRequest = new com.aliyun.vod20170321.models.DecryptKMSDataKeyRequest();
decryptKMSDataKeyRequest.setCipherText(kms);
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
try {
// 复制代码运行请自行打印 API 的返回值
DecryptKMSDataKeyResponse decryptKMSDataKeyResponse = client.decryptKMSDataKeyWithOptions(decryptKMSDataKeyRequest, runtime);
return decryptKMSDataKeyResponse.getBody();
} catch (TeaException error) {
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
System.out.println(error.getMessage());
// 诊断地址
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
return null;
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
// 错误 message
System.out.println(error.getMessage());
// 诊断地址
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
return null;
}
}
public static AssumeRoleResponse assumeRole() throws ClientException { public static AssumeRoleResponse assumeRole() throws ClientException {

View File

@@ -0,0 +1,9 @@
package com.peanut.modules.common.dao;
import com.github.yulichang.base.MPJBaseMapper;
import com.peanut.modules.common.entity.VideoM3u8Entity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface VideoM3u8Dao extends MPJBaseMapper<VideoM3u8Entity> {
}

View File

@@ -38,4 +38,6 @@ public class CourseCatalogueChapterVideoEntity {
private String videoUrl; private String videoUrl;
@TableField(exist = false) @TableField(exist = false)
private String Mp4Url; private String Mp4Url;
@TableField(exist = false)
private String MtsHlsUriToken;
} }

View File

@@ -0,0 +1,19 @@
package com.peanut.modules.common.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("video_m3u8")
public class VideoM3u8Entity {
@TableId
private Integer id;
private String vid;
private String edk;
private Integer state;
}

View File

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.peanut.common.service.AsyncService; import com.peanut.common.service.AsyncService;
import com.peanut.common.utils.PlayToken;
import com.peanut.common.utils.R; import com.peanut.common.utils.R;
import com.peanut.common.utils.ShiroUtils; import com.peanut.common.utils.ShiroUtils;
import com.peanut.common.utils.SpdbUtil; import com.peanut.common.utils.SpdbUtil;
@@ -43,6 +44,8 @@ public class CourseCatalogueChapterVideoServiceImpl extends ServiceImpl<CourseCa
private CourseToMedicineDao courseToMedicineDao; private CourseToMedicineDao courseToMedicineDao;
@Autowired @Autowired
private MyUserDao userDao; private MyUserDao userDao;
@Autowired
private PlayToken playToken;
@Override @Override
public Page getCourseCatalogueChapterVideoList(ParamTo param) { public Page getCourseCatalogueChapterVideoList(ParamTo param) {
@@ -105,6 +108,8 @@ public class CourseCatalogueChapterVideoServiceImpl extends ServiceImpl<CourseCa
GetVideoPlayAuthResponse p = SpdbUtil.getPlayAuth(video.getVideo()); GetVideoPlayAuthResponse p = SpdbUtil.getPlayAuth(video.getVideo());
String playAuth = p.getBody().getPlayAuth(); String playAuth = p.getBody().getPlayAuth();
video.setPlayAuth(playAuth); video.setPlayAuth(playAuth);
String s = playToken.generateToken();
video.setMtsHlsUriToken(s);
} }
UserCourseVideoPositionEntity videoPosition = getVideoPosition(video, uId); UserCourseVideoPositionEntity videoPosition = getVideoPosition(video, uId);
video.setUserCourseVideoPositionEntity(videoPosition); video.setUserCourseVideoPositionEntity(videoPosition);

View File

@@ -200,11 +200,22 @@ public class CourseController {
@RequestMapping("/mytt") @RequestMapping("/mytt")
public R mytt() throws Exception { public R mytt() throws Exception {
String s = playToken.generateToken(); // String s = playToken.generateToken();
System.out.println(s); // System.out.println(s);
boolean b = playToken.validateToken(s); // boolean b = playToken.validateToken(s);
System.out.println(b); // System.out.println(b);
return R.ok();
GenerateKMSDataKeyResponseBody kms = SpdbUtil.KMS();
return R.ok().put("result",kms);
// return R.ok();
}
@RequestMapping("/mytt1")
public R mytt1(@RequestBody Map<String,String> map){
DecryptKMSDataKeyResponseBody decryptKMSDataKeyResponseBody = SpdbUtil.enKMS(map.get("kms"));
return R.ok().put("result",decryptKMSDataKeyResponseBody);
} }
@RequestMapping("/ttt") @RequestMapping("/ttt")