SpringBoot项目如何通过代码端管控华为云存储媒体的CDN访问权限
解决方案:SpringBoot代码端阻止未授权访问华为云CDN媒体资源
核心思路是不直接暴露永久有效的CDN地址,通过代码层做权限校验后,提供临时可访问路径或代理资源,具体方案如下:
方案1:生成带签名的临时CDN访问地址
华为云CDN支持URL签名鉴权,你可以在代码中完成权限校验后,生成带有效期的签名URL返回给前端,未授权用户拿到过期或无签名的地址无法访问。
实现步骤:
- 权限校验:用户请求资源时,先校验登录态、角色或资源归属(比如通过Token判断用户是否有权限查看该媒体)。
- 生成签名URL:调用自定义工具类,按照华为云CDN签名规则生成带过期时间的URL。
- 返回临时地址:把生成的临时URL返回给前端,前端用这个地址请求CDN。
简化代码示例:
import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Base64; public class CdnSignUtils { // 华为云CDN配置的密钥,存在application.yml中 private static final String CDN_SECRET_KEY = "${your.cdn.secret.key}"; public static String generateSignedUrl(String originalCdnUrl, long expireMinutes) throws Exception { URL url = new URL(originalCdnUrl); String uri = url.getPath(); // 计算过期时间(秒级时间戳) long expireTime = System.currentTimeMillis() / 1000 + expireMinutes * 60; // 构造签名原始串:uri + 过期时间 String signRaw = uri + expireTime; // 用HMAC-SHA256生成签名 Mac mac = Mac.getInstance("HmacSHA256"); SecretKeySpec secretKey = new SecretKeySpec(CDN_SECRET_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); mac.init(secretKey); byte[] signBytes = mac.doFinal(signRaw.getBytes(StandardCharsets.UTF_8)); String signature = Base64.getUrlEncoder().encodeToString(signBytes); // 拼接带签名的URL return originalCdnUrl + "?sign=" + signature + "&expire=" + expireTime; } }
注意:
- 密钥不要硬编码,通过SpringBoot配置文件注入;
- 过期时间根据业务调整(比如5分钟、1小时),平衡安全性和用户体验。
方案2:SpringBoot代理资源请求
前端不直接请求CDN,而是请求你的SpringBoot接口,接口先做权限校验,通过后再从CDN拉取资源返回给前端,完全隐藏CDN地址。
实现步骤:
- 接口定义:写一个资源获取接口,接收资源ID或标识;
- 权限校验:验证用户是否有权访问该资源;
- 代理请求:后端请求华为云CDN获取资源流,直接输出到前端响应。
简化代码示例:
import org.springframework.http.HttpStatus; import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @RestController public class MediaController { @GetMapping("/media/{resourceId}") public void getMedia(@PathVariable String resourceId, HttpServletResponse response) throws IOException { // 1. 权限校验:自定义逻辑判断用户是否有权访问该资源 if (!checkUserPermission(resourceId)) { response.setStatus(HttpStatus.FORBIDDEN.value()); return; } // 2. 根据resourceId从数据库/配置中获取原始CDN地址 String cdnUrl = getCdnUrlByResourceId(resourceId); // 3. 拉取CDN资源并返回给前端 RestTemplate restTemplate = new RestTemplate(); var resourceResponse = restTemplate.getForEntity(cdnUrl, org.springframework.core.io.Resource.class); // 设置响应头,保持资源原始类型 response.setContentType(resourceResponse.getHeaders().getContentType().toString()); // 流式传输,避免大文件内存溢出 StreamUtils.copy(resourceResponse.getBody().getInputStream(), response.getOutputStream()); } // 自定义权限校验方法 private boolean checkUserPermission(String resourceId) { // 示例:校验用户Token、资源归属等 return true; } // 自定义方法:根据资源ID获取CDN地址 private String getCdnUrlByResourceId(String resourceId) { // 示例:从数据库查询或配置中读取 return "https://your-cdn-domain.com/" + resourceId; } }
注意:
- 大文件场景推荐用流式传输,避免OOM;
- 可添加缓存逻辑,减少重复请求CDN的开销。
方案3:CDN回源鉴权(结合代码层校验)
配置华为云CDN回源到你的SpringBoot服务,当用户访问CDN地址时,CDN会先回源到你的服务做权限校验,通过后才返回资源。
实现步骤:
- CDN配置:在华为云CDN控制台设置回源规则,将资源请求回源到你的SpringBoot服务地址;
- 拦截器/过滤器:在SpringBoot中编写拦截器,拦截CDN的回源请求,校验用户身份(比如Cookie、Token);
- 权限判断:校验通过则放行,让CDN缓存资源;不通过返回403。
注意:
- 要识别CDN回源请求的标识头(比如华为云CDN的
X-Cdn-Source),避免拦截普通用户请求; - 确保回源请求的安全性,防止伪造回源请求。
方案选择建议
- 优先选方案1:实现简单、性能开销小,适合大多数场景;
- 需要实时校验权限或完全隐藏CDN地址,选方案2;
- 需统一管控所有CDN资源访问,选方案3。
内容的提问来源于stack exchange,提问作者dempsey
相关产品推荐
相关产品推荐

