本文介绍VodAppServer的高级特性与使用场景。
JWT播放鉴权
重要
使用JWT播放鉴权需集成播放器V7.10.0及以上版本。
工作原理
VodAppServer使用JWT(JSON Web Token)实现播放鉴权:服务端通过PlayKey本地签名生成playAuth,经视频点播校验后返回视频流。

生成播放凭证(服务端本地签名)
生成示例:
// 核心代码
String playAuth = JwtUtil.getPlayAuthToken(videoId, playKey);说明
playKey可参考playKey是什么获取。
自定义Token有效期
// 默认有效期:3600秒(1小时)
String playAuth = JwtUtil.getPlayAuthToken(videoId, playKey);
// 自定义有效期:7200秒(2小时)
String playAuth = JwtUtil.getPlayAuthToken(videoId, playKey, 7200);配置播放密钥
获取播放密钥
播放密钥获取请参考playKey是什么。
设置播放密钥
// 调用接口设置
SetAppPlayKeyResponse response = vodSdkService.SetAppPlayKey(
JwtConstants.DEFAULT_APP_ID,
"your_new_play_key"
);安全与性能
AccessKey安全
推荐使用以下三种方式配置AccessKey,任选其一即可。
使用环境变量。
# 设置环境变量 export ALIYUN_AK=your_access_key export ALIYUN_SK=your_secret_key# 配置文件引用 aliyun: vod: ak: ${ALIYUN_AK} sk: ${ALIYUN_SK} region: ${ALIYUN_VOD_REGION:cn-shanghai} # 地域标识,支持环境变量使用配置中心。
@Configuration public class VodConfigLoader { @Value("${config.center.url}") private String configCenterUrl; @Bean public VodConfig vodConfig() { // 从配置中心加载 return configClient.load("vod-config"); } }使用阿里云RAM角色。
// 使用 ECS 实例角色 // 注意:region 需要从配置中读取,不能硬编码 String regionId = vodConfig.getRegion(); // 从配置读取 DefaultProfile profile = DefaultProfile.getProfile( regionId // 不需要 AK/SK,自动使用实例角色 );
接口限流
@Component
public class RateLimiter {
private final Semaphore semaphore = new Semaphore(100); // 并发限制
public <T> T execute(Supplier<T> action) throws InterruptedException {
semaphore.acquire();
try {
return action.get();
} finally {
semaphore.release();
}
}
}缓存策略
@Service
public class CachedVodService {
@Cacheable(value = "playlistCache", key = "#playlistId", unless = "#result == null")
public PlayList getPlaylist(String playlistId) {
return vodSdkService.getPlaylist(playlistId);
}
@CacheEvict(value = "playlistCache", key = "#playlistId")
public void updatePlaylist(String playlistId, PlayList playlist) {
vodSdkService.updatePlaylist(playlistId, playlist);
}
}异步处理
@Service
public class AsyncVideoProcessor {
@Async("vodExecutor")
public CompletableFuture<String> generatePlayAuth(String videoId) {
String playAuth = JwtUtil.getPlayAuthToken(videoId, playKey);
return CompletableFuture.completedFuture(playAuth);
}
public List<String> batchGeneratePlayAuth(List<String> videoIds) {
List<CompletableFuture<String>> futures = videoIds.stream()
.map(this::generatePlayAuth)
.collect(Collectors.toList());
return futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
}扩展开发
自定义拦截器
@Component
public class VodAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 验证请求签名
String signature = request.getHeader("X-Signature");
if (!validateSignature(signature)) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return false;
}
return true;
}
private boolean validateSignature(String signature) {
// 实现签名验证逻辑
return true;
}
}自定义异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ClientException.class)
public ResponseEntity<CallResult> handleClientException(ClientException e) {
CallResult result = new CallResult();
// 这里示例直接复用 SYSTEM_INNER_ERROR,可根据需要在 ResultCode 中扩展专门的 API 错误码
result.setCode(ResultCode.SYSTEM_INNER_ERROR.code);
result.setHttpCode("500");
result.setSuccess(false);
result.setMessage("阿里云 API 调用失败: " + e.getMessage());
return ResponseEntity.status(500).body(result);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<CallResult> handleException(Exception e) {
CallResult result = new CallResult();
result.setCode(ResultCode.SYSTEM_INNER_ERROR.code);
result.setHttpCode("500");
result.setSuccess(false);
result.setMessage("系统错误: " + e.getMessage());
return ResponseEntity.status(500).body(result);
}
}性能监控
接口耗时统计
@Aspect
@Component
public class PerformanceMonitor {
@Around("execution(* com.aliyun.appserver.controller..*(..))")
public Object monitorPerformance(ProceedingJoinPoint pjp) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = pjp.proceed();
long duration = System.currentTimeMillis() - startTime;
String methodName = pjp.getSignature().getName();
if (duration > 1000) {
log.warn("接口 {} 耗时过长: {}ms", methodName, duration);
}
return result;
}
}日志记录
@Slf4j
@Component
public class ApiLogger {
public void logRequest(HttpServletRequest request) {
log.info("API 请求 - 方法: {}, URI: {}, 参数: {}",
request.getMethod(),
request.getRequestURI(),
request.getParameterMap()
);
}
public void logResponse(CallResult result, long duration) {
log.info("API 响应 - 状态: {}, 耗时: {}ms, 消息: {}",
result.getSuccess(),
duration,
result.getMessage()
);
}
}该文章对您有帮助吗?