소스 정보
- 저장소
- doccker/cc-use-exp
- 최근 소스 활동
- 2026년 6월 30일 10:04
- 감지된 SKILL.md 언어
- 중국어
- 스타
- 1,006
- 포크
- 103
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/doccker/cc-use-exp --skill storage-url-safety명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
网关/代理/WAF/CDN 中间件的安全关键词匹配实现规范,防止纯子串匹配误判正常响应内容中的技术术语(如 Cloudflare、502、error)
涉及浏览器、编辑器、CDN/WAF、IM 平台、操作系统剪贴板、第三方 SaaS 等"外部黑盒系统"的代码编写或 bug 调试时触发。强制先抓真实环境数据再推理,避免连续 2 轮"凭代码推理"的修复 no-op。关键词:粘贴/复制异常、跨平台显示不一致、第三方 API 怪结果、CDN/WAF 拦截、本地复现失败、HTML→MD 转换丢属性。
前端开发规范,包含 Vue 3 编码规范、UI 风格约束、TypeScript 规范等
| name | storage-url-safety |
| description | 当使用 MinIO/OSS/S3 等对象存储、设计文件上传下载功能时触发。提供存储 URL 策略选择规范,防止 URL 过期、访问失败等问题。 |
当使用 MinIO/OSS/S3 等对象存储时,正确选择 URL 生成策略。
场景: 头像、Logo、商品图等需要长期访问的资源
预签名 URL 有时效限制(MinIO/S3 最大 7 天),头像等长期资源会过期导致无法访问。
// ❌ 错误: 预签名 URL 最大 7 天,头像会过期
String avatarUrl = minioService.getPresignedUrl(filePath, 60 * 24 * 365);
// IllegalArgumentException: expiry must be minimum 1 second to maximum 7 days
// ❌ 错误: 即使设置 7 天,头像也会在 7 天后失效
String avatarUrl = minioService.getPresignedUrl(filePath, 60 * 24 * 7);
// 7 天后用户头像显示"图片加载失败"
// ✅ 方案1: 公开 URL(需配置 bucket 公开读)
public String getPublicUrl(String filePath) {
String endpoint = minioConfig.getEndpoint();
if (endpoint.endsWith("/")) {
endpoint = endpoint.substring(0, endpoint.length() - 1);
}
return endpoint + "/" + minioConfig.getBucketName() + "/" + filePath;
}
String avatarUrl = minioService.getPublicUrl(filePath);
// 返回: http://minio:9000/bucket/avatars/xxx.jpeg
// ✅ 方案2: CDN URL(生产环境推荐)
String avatarUrl = cdnService.getCdnUrl(filePath);
// 返回: https://cdn.example.com/avatars/xxx.jpeg
| 资源类型 | 推荐策略 | 有效期 | 适用场景 | 示例 |
|---|---|---|---|---|
| 头像/Logo | 公开 URL / CDN | 永久 | 需长期访问 | 用户头像、企业 Logo |
| 商品图片 | 公开 URL / CDN | 永久 | 需长期访问 | 电商商品图、文章配图 |
| 公开文档 | 公开 URL / CDN | 永久 | 需长期访问 | 用户手册、API 文档 |
| 临时文件 | 预签名 URL | 1h-7d | 下载凭证 | 导出的 Excel、临时分享 |
| 私密文档 | 预签名 URL | 15min-1h | 临时授权 | 合同、财务报表 |
| 上传凭证 | 预签名 URL | 5min-30min | 客户端直传 | 前端直传 OSS |
场景: 使用公开 URL 但 bucket 策略未配置
// ✅ 代码正确生成公开 URL
String avatarUrl = minioService.getPublicUrl(filePath);
// 返回: http://minio:9000/bucket/avatars/xxx.jpeg
// ❌ 但 bucket 未配置公开读,访问返回 403 Forbidden
MinIO 配置公开读:
# 方案1: 使用 mc 命令配置(推荐)
mc anonymous set download minio/bucket/avatars
# 方案2: 使用 bucket policy
mc admin policy attach minio readonly --user=public
Bucket Policy 示例:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": ["*"]},
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::bucket/avatars/*"]
}
]
}
阿里云 OSS 配置:
# 设置 bucket 公共读
ossutil64 set-acl oss://bucket-name public-read
# 或只设置特定目录
ossutil64 set-acl oss://bucket-name/avatars/ public-read --recursive
场景: 临时文件下载链接有效期过长或过短
| 场景 | 推荐有效期 | 说明 |
|---|---|---|
| 客户端直传凭证 | 5-30 分钟 | 上传时间通常很短 |
| 临时分享链接 | 1-24 小时 | 用户可能稍后下载 |
| 导出文件下载 | 1-7 天 | 用户可能多次下载 |
| 私密文档查看 | 15-60 分钟 | 安全性要求高 |
// ❌ 错误: 客户端直传凭证有效期 7 天,安全风险高
String uploadUrl = minioService.getPresignedUrl(filePath, 60 * 24 * 7);
// ❌ 错误: 导出文件下载链接只有 5 分钟,用户可能来不及下载
String downloadUrl = minioService.getPresignedUrl(filePath, 5);
// ✅ 客户端直传凭证: 15 分钟
String uploadUrl = minioService.getPresignedUrl(filePath, 15);
// ✅ 导出文件下载: 24 小时
String downloadUrl = minioService.getPresignedUrl(filePath, 60 * 24);
// ✅ 私密文档查看: 30 分钟
String viewUrl = minioService.getPresignedUrl(filePath, 30);
场景: 前端直传 OSS,后端生成上传凭证
// ❌ 错误: 未校验文件类型和大小,任何文件都能上传
@PostMapping("/upload/token")
public ApiResponse<String> getUploadToken(@RequestParam String filename) {
String uploadUrl = minioService.getPresignedUrl("uploads/" + filename, 15);
return ApiResponse.success(uploadUrl);
}
// ✅ 后端校验文件类型和大小
@PostMapping("/upload/token")
public ApiResponse<UploadToken> getUploadToken(
@RequestParam String filename,
@RequestParam String contentType,
@RequestParam Long fileSize) {
// 校验文件类型
List<String> allowedTypes = Arrays.asList("image/jpeg", "image/png", "image/gif");
if (!allowedTypes.contains(contentType)) {
return ApiResponse.error("不支持的文件类型");
}
// 校验文件大小(5MB)
if (fileSize > 5 * 1024 * 1024) {
return ApiResponse.error("文件大小不能超过 5MB");
}
// 生成安全的文件名(防止路径遍历)
String safeFilename = UUID.randomUUID() + getExtension(filename);
String filePath = "avatars/" + LocalDate.now() + "/" + safeFilename;
String uploadUrl = minioService.getPresignedUrl(filePath, 15);
return ApiResponse.success(new UploadToken(uploadUrl, filePath));
}
场景: 使用 CDN 加速但回源配置不正确
// ✅ 代码正确返回 CDN URL
String avatarUrl = "https://cdn.example.com/avatars/xxx.jpeg";
// ❌ 但 CDN 回源配置错误:
// 1. 回源 Host 未设置为 MinIO endpoint
// 2. 回源协议未设置为 HTTP
// 3. 回源路径未包含 bucket 名称
// 导致 CDN 返回 404 或 403
阿里云 CDN 回源配置:
回源 Host: minio.example.com
回源协议: HTTP
回源地址: minio.example.com:9000
回源路径: /bucket${uri}
腾讯云 CDN 回源配置:
源站类型: 自有源
源站地址: minio.example.com:9000
回源协议: HTTP
回源 Host: minio.example.com
回源路径: /bucket${uri}
场景: 数据库存储相对路径(如 images/4/2026-04/xxx.png),多个 Service 各自写一份 resolveImageUrl 方法将 path 转为可访问 URL
当"数据库存 path、响应时补全 URL"成为项目约定后,每个返回图片字段的 Service 都需要做 URL 补全。如果没有统一工具方法,就会出现:
resolveImageUrl// ❌ 错误: 每个 Service 各写一份
// ProductService.java
private String resolveImageUrl(String imageUrl) {
if (imageUrl == null || imageUrl.startsWith("http")) return imageUrl;
return minioService.getPresignedUrl(imageUrl, 60 * 24 * 7);
}
// MiniProductService.java — 又写一份
private String resolveImageUrl(String imageUrl) { /* 同样逻辑 */ }
// CartService.java — 又写一份
private String resolveImageUrl(String imageUrl) { /* 同样逻辑 */ }
// MiniOrderService.java — 又写一份...
// ✅ 正确: 抽成共享工具类,所有 Service 复用
@Component
public class ImageUrlResolver {
private final MinioService minioService;
public String resolve(String imageUrl) {
if (imageUrl == null || imageUrl.isBlank()) return imageUrl;
if (imageUrl.startsWith("http://") || imageUrl.startsWith("https://")) {
return imageUrl;
}
return minioService.getPresignedUrl(imageUrl, 60 * 24 * 7);
}
public List<String> resolveAll(List<String> urls) {
if (urls == null) return List.of();
return urls.stream().map(this::resolve).toList();
}
}
// 各 Service 注入后直接用
@Service
public class ProductService {
private final ImageUrlResolver imageUrlResolver;
private ProductDTO convertToDTO(Product product) {
return ProductDTO.builder()
.mainImageUrl(imageUrlResolver.resolve(mainImagePath))
.build();
}
}
getImageUrl)再一次性补齐URL 策略选择:
Bucket 配置:
CDN 配置:
安全性:
响应层 URL 补全:
> 📋 本回复遵循:`storage-url-safety` - 存储 URL 策略选择规范