news 2026/3/26 20:48:49

阿里云 OSS + STS:安全的文件上传方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
阿里云 OSS + STS:安全的文件上传方案

阿里云 OSS + STS:安全的文件上传方案

一、引言

在 IM 系统中,文件上传是一个常见需求。用户需要上传图片、音频、视频等文件。传统的做法是将文件先上传到应用服务器,再由服务器转发到云存储,这种方式存在性能瓶颈和安全风险。更优的方案是让客户端直接上传到云存储,但如何保证安全性?本文将介绍如何使用阿里云 OSS + STS 实现安全的文件上传方案

二、为什么使用 STS 而不是永久密钥?

2.1 永久密钥的安全风险

传统方案的问题

如果使用永久 AccessKey(主账号密钥),通常有两种方式:

方案 1:密钥放在客户端
// ❌ 危险:密钥暴露在客户端代码中constclient=newOSS({accessKeyId:'LTAI5t...',// 永久密钥accessKeySecret:'xxx...',// 永久密钥bucket:'my-bucket',region:'oss-cn-shenzhen'});

风险

  • 密钥暴露在客户端代码中,任何人都可以获取
  • 一旦泄露,攻击者可以完全控制 OSS 资源
  • 无法限制权限,只能使用主账号的全部权限
  • 无法撤销,只能更换密钥(影响所有服务)
方案 2:密钥放在服务端,客户端通过服务端上传
客户端 → 服务端 → OSS

问题

  • 服务端成为性能瓶颈,需要中转所有文件
  • 占用服务端带宽和存储资源
  • 上传大文件时响应慢,用户体验差

2.2 STS 临时凭证的优势

STS(Security Token Service) 是阿里云提供的临时访问凭证服务,具有以下优势:

对比项永久密钥STS 临时凭证
安全性低(永久有效)高(临时有效,自动过期)
权限控制主账号全部权限可精确控制权限范围
泄露影响严重(需更换密钥)轻微(自动过期)
性能需服务端中转客户端直传,性能好
灵活性高(可动态生成)

核心优势

  1. 临时性:凭证有效期短(通常 1 小时),过期自动失效
  2. 权限最小化:可以精确控制允许的操作和资源范围
  3. 安全性高:即使泄露,影响范围和时间都有限
  4. 性能好:客户端直接上传到 OSS,不经过服务端

2.3 实际案例对比

场景:用户上传图片
使用永久密钥(不安全)

风险:密钥泄露 → 攻击者可以: - 上传任意文件 - 删除所有文件 - 修改文件权限 - 造成数据泄露和经济损失

使用 STS 临时凭证(安全)

优势: - 凭证 1 小时后自动失效 - 只能上传到指定目录 - 只能执行上传操作 - 即使泄露,影响范围有限

三、STS 临时凭证的获取流程

3.1 整体流程

┌─────────┐ ① 请求临时凭证 ┌─────────┐ │ 客户端 │ ────────────────────────→ │ 服务端 │ │ │ │ │ │ │ ② 调用 STS API │ │ │ │ ←────────────────────────── │ │ │ │ │ │ │ │ ③ 返回临时凭证 │ │ │ │ ←────────────────────────── │ │ │ │ │ │ │ │ ④ 直接上传到 OSS │ │ │ │ ────────────────────────→ │ 阿里云OSS│ │ │ │ │ │ │ ⑤ 返回文件 URL │ │ │ │ ←────────────────────────── │ │ └─────────┘ └─────────┘

3.2 详细步骤

步骤 1:客户端请求临时凭证
// 客户端发送请求 message GetStsCmd { MsgType msgType = 1; // 消息类型(图片/音频/视频等) }
步骤 2:服务端调用 STS API

服务端使用主账号的 AccessKey 调用 STS 服务,获取临时凭证:

@ComponentpublicclassAliOssProvider{publicAliOssStsDtogetAliOssSts(){// 1. 配置 STS 客户端Stringendpoint="sts.cn-hangzhou.aliyuncs.com";StringaccessKeyId="主账号AccessKeyId";StringaccessKeySecret="主账号AccessKeySecret";DefaultProfile.addEndpoint("","Sts",endpoint);IClientProfileprofile=DefaultProfile.getProfile("",accessKeyId,accessKeySecret);DefaultAcsClientclient=newDefaultAcsClient(profile);// 2. 构建 AssumeRole 请求AssumeRoleRequestrequest=newAssumeRoleRequest();request.setSysMethod(MethodType.POST);request.setRoleArn("acs:ram::123456789:role/oss-upload-role");// RAM 角色request.setRoleSessionName("upload-session");// 会话名称request.setDurationSeconds(3600L);// 有效期 1 小时// 3. 调用 STS APIAssumeRoleResponseresponse=client.getAcsResponse(request);// 4. 提取临时凭证AliOssStsDtostsDto=newAliOssStsDto();stsDto.setAccessKeyId(response.getCredentials().getAccessKeyId());stsDto.setAccessKeySecret(response.getCredentials().getAccessKeySecret());stsDto.setSecurityToken(response.getCredentials().getSecurityToken());stsDto.setBucket("my-bucket");stsDto.setRegion("oss-cn-shenzhen");stsDto.setOssEndpoint("oss-cn-shenzhen.aliyuncs.com");returnstsDto;}}
步骤 3:返回临时凭证给客户端
// 服务端返回 message GetStsAck { string accessKeyId = 1; // 临时 AccessKeyId string accessKeySecret = 2; // 临时 AccessKeySecret string securityToken = 3; // 安全令牌 string region = 4; // 区域 string bucket = 5; // 存储桶名称 string uploadPath = 6; // 上传路径(如:image/2024/01/15) string endpoint = 7; // OSS 端点 }
步骤 4:客户端直接上传到 OSS
// 客户端使用临时凭证上传constclient=newOSS({accessKeyId:stsDto.accessKeyId,accessKeySecret:stsDto.accessKeySecret,stsToken:stsDto.securityToken,// 关键:临时令牌bucket:stsDto.bucket,region:stsDto.region,endpoint:stsDto.endpoint});// 上传文件constresult=await client.put(`${stsDto.uploadPath}/${fileName}`,// 完整路径file);// 获取文件 URLconstfileUrl=result.url;

3.3 关键配置说明

RAM 角色配置(在阿里云控制台)

  1. 创建 RAM 角色
  • 角色名称:oss-upload-role
  • 信任实体:当前账号
  1. 配置角色权限策略
{"Version":"1","Statement":[{"Effect":"Allow","Action":["oss:PutObject","oss:GetObject"],"Resource":["acs:oss:*:*:my-bucket/image/*","acs:oss:*:*:my-bucket/audio/*","acs:oss:*:*:my-bucket/video/*"]}]}

权限说明

  • oss:PutObject:允许上传文件
  • oss:GetObject:允许下载文件
  • Resource:限制只能操作指定路径下的文件
  1. 获取角色 ARN
格式:acs:ram::{账号ID}:role/{角色名称} 示例:acs:ram::123456789:role/oss-upload-role

四、凭证缓存机制的设计

4.1 为什么需要缓存?

问题场景

  • 用户上传多个文件时,每次都请求 STS 会:
    • 增加 STS API 调用次数(有频率限制)
    • 增加响应延迟(每次 100-200ms)
    • 增加服务端负载

解决方案

  • 缓存临时凭证,多个请求共享同一个凭证
  • 减少 STS API 调用
  • 提升响应速度

4.2 缓存设计

在 AQChat 项目中,我们使用 Redis 缓存临时凭证:

@ComponentpublicclassAliOssProvider{@ResourceprivateRedisCacheHelperredisCacheHelper;privatestaticfinallongCACHE_TIME=3600-60;// 缓存 59 分钟(比凭证有效期少 1 分钟)publicAliOssStsDtogetAliOssSts(){// 1. 先尝试从缓存获取AliOssStsDtocachedSts=getCacheAliOssSts();if(cachedSts!=null){LOGGER.info("从缓存获取 STS 凭证");returncachedSts;}// 2. 缓存未命中,调用 STS APIAliOssStsDtostsDto=callStsApi();// 3. 存入缓存if(stsDto!=null){cacheAliOssSts(stsDto);}returnstsDto;}/** * 从缓存获取临时凭证 */privateAliOssStsDtogetCacheAliOssSts(){returnredisCacheHelper.getCacheObject(AQRedisKeyPrefix.ALI_OSS_STS,AliOssStsDto.class);}/** * 缓存临时凭证 */privatevoidcacheAliOssSts(AliOssStsDtostsDto){redisCacheHelper.setCacheObject(AQRedisKeyPrefix.ALI_OSS_STS,stsDto,CACHE_TIME,TimeUnit.SECONDS);}}

4.3 缓存策略

缓存 Key

Key: AQChat:aliOssSts Value: AliOssStsDto (JSON 序列化) TTL: 3540 秒(59 分钟)

缓存时间设计

项目时间说明
STS 凭证有效期3600 秒(1 小时)阿里云 STS 返回的凭证有效期
缓存 TTL3540 秒(59 分钟)比凭证有效期少 1 分钟
安全边界60 秒确保凭证过期前缓存已失效

设计原因

  • 缓存时间略短于凭证有效期,避免返回已过期的凭证
  • 留出 1 分钟的安全边界,确保凭证在使用时仍然有效

4.4 缓存效果

性能提升

指标无缓存有缓存提升
STS API 调用每次请求每小时 1 次减少 99%+
响应时间150ms< 1ms提升 150 倍
并发支持受 STS 限流影响不受限显著提升

实际测试数据

  • 1000 个并发请求,无缓存:需要 1000 次 STS 调用,部分请求可能被限流
  • 1000 个并发请求,有缓存:只需要 1 次 STS 调用,所有请求从缓存获取

五、文件路径的组织策略

5.1 路径设计原则

  1. 按类型分类
  • 不同类型的文件存储在不同目录
  • 便于管理和权限控制
  1. 按时间分层
  • 使用日期组织文件
  • 便于清理和归档
  1. 避免冲突
  • 使用唯一文件名(UUID)
  • 防止文件覆盖

5.2 路径组织方案

在 AQChat 项目中,我们采用以下路径组织策略:

@ComponentpublicclassGetStsCmdHandler{publicvoidhandle(ChannelHandlerContextctx,GetStsCmdcmd){// 1. 获取消息类型intmsgTypeValue=cmd.getMsgTypeValue();StringmsgType=getMsgTypeByCode(msgTypeValue);// msgType: "image" / "audio" / "video" / "file"// 2. 生成上传路径StringuploadPath=msgType+"/"+getFormatTime();// 示例:image/2024/01/15// 3. 设置到 STS 凭证中aliOssSts.setUploadPath(uploadPath);}privateStringgetFormatTime(){SimpleDateFormatsdf=newSimpleDateFormat("yyyy/MM/dd");returnsdf.format(newDate());}}

路径结构

bucket/ ├── image/ # 图片文件 │ ├── 2024/ │ │ ├── 01/ │ │ │ ├── 15/ │ │ │ │ ├── uuid1.jpg │ │ │ │ ├── uuid2.png │ │ │ │ └── ... │ │ │ └── 16/ │ │ │ └── ... │ │ └── 02/ │ │ └── ... │ └── ... ├── audio/ # 音频文件 │ ├── 2024/ │ │ └── ... │ └── ... ├── video/ # 视频文件 │ ├── 2024/ │ │ └── ... │ └── ... └── file/ # 其他文件 ├── 2024/ │ └── ... └── ...

5.3 路径设计优势

  1. 便于管理
  • 按类型分类,快速定位文件类型
  • 按日期分层,便于按时间范围清理
  1. 权限控制
  • 可以为不同目录设置不同的访问权限
  • 例如:图片公开访问,文件需要认证
  1. 性能优化
  • 文件分散在不同目录,避免单目录文件过多
  • OSS 单目录文件过多会影响性能
  1. 成本控制
  • 可以按目录设置生命周期规则
  • 例如:1 年以上的文件自动转为归档存储

5.4 完整文件路径生成

客户端上传时:

// 1. 获取 STS 凭证(包含 uploadPath)conststsResponse=awaitgetStsCredentials(msgType);// stsResponse.uploadPath = "image/2024/01/15"// 2. 生成唯一文件名constfileName=`${generateUUID()}.${getFileExtension(file)}`;// fileName = "550e8400-e29b-41d4-a716-446655440000.jpg"// 3. 组合完整路径constfullPath=`${stsResponse.uploadPath}/${fileName}`;// fullPath = "image/2024/01/15/550e8400-e29b-41d4-a716-446655440000.jpg"// 4. 上传到 OSSconstresult=await ossClient.put(fullPath,file);

六、安全性考虑

6.1 多层安全防护

  1. 临时凭证有效期限制
  • 凭证有效期 1 小时,过期自动失效
  • 即使泄露,影响时间有限
  1. 权限最小化原则
{"Statement":[{"Effect":"Allow","Action":["oss:PutObject"],// 只允许上传"Resource":["acs:oss:*:*:bucket/image/*"]// 只能上传到指定目录}]}
  • 只授予必要的权限(上传)
  • 限制资源范围(指定目录)
  • 禁止删除、修改等危险操作
  1. 路径限制
  • 服务端控制上传路径
  • 客户端无法修改路径,防止目录遍历攻击
  • 路径包含日期,便于审计和追踪
  1. 文件类型验证(可选)
// 服务端可以验证文件类型privatebooleanisValidFileType(StringfileName,MsgTypemsgType){Stringextension=getFileExtension(fileName);switch(msgType){caseIMAGE:returnArrays.asList("jpg","jpeg","png","gif").contains(extension);caseAUDIO:returnArrays.asList("mp3","wav","aac").contains(extension);caseVIDEO:returnArrays.asList("mp4","avi","mov").contains(extension);default:returnfalse;}}

6.2 防止常见攻击

  1. 目录遍历攻击
❌ 危险路径:../../../etc/passwd ✅ 安全路径:image/2024/01/15/uuid.jpg
  • 服务端控制路径,客户端无法构造危险路径
  1. 文件覆盖攻击
✅ 使用 UUID 作为文件名,避免冲突 ✅ 路径包含时间,进一步降低冲突概率
  1. 大文件攻击
// 可以在 RAM 策略中限制文件大小 { "Condition": { "NumericLessThan": { "oss:ContentLength": 10485760 // 限制 10MB } } }
  1. 恶意文件上传
  • 客户端上传后,服务端可以异步扫描文件
  • 发现恶意文件可以及时删除
  • 使用 OSS 的病毒扫描功能

6.3 安全最佳实践

  1. 主账号密钥保护
  • 主账号 AccessKey 只存储在服务端
  • 使用环境变量或密钥管理服务(如阿里云 KMS)
  • 定期轮换密钥
  1. RAM 角色权限最小化
  • 只授予必要的权限
  • 定期审查和更新权限策略
  • 使用条件限制(如 IP、时间等)
  1. 监控和告警
  • 监控 STS API 调用频率
  • 监控异常上传行为
  • 设置告警规则
  1. 日志记录
LOGGER.info("用户{}获取STS凭证,类型:{},路径:{}",userId,msgType,uploadPath);
  • 记录所有 STS 凭证获取请求
  • 记录文件上传操作
  • 便于审计和问题排查

七、完整实现示例

7.1 服务端实现

@ComponentpublicclassAliOssProvider{@ResourceprivateAQChatConfigconfig;@ResourceprivateRedisCacheHelperredisCacheHelper;privatestaticfinallongCACHE_TIME=3600-60;// 59 分钟/** * 获取临时凭证(带缓存) */publicAliOssStsDtogetAliOssSts(){// 1. 从缓存获取AliOssStsDtocached=getCacheAliOssSts();if(cached!=null){returncached;}// 2. 调用 STS APIAliOssStsDtostsDto=callStsApi();// 3. 缓存结果if(stsDto!=null){cacheAliOssSts(stsDto);}returnstsDto;}/** * 调用 STS API 获取临时凭证 */privateAliOssStsDtocallStsApi(){try{// 配置 STS 客户端DefaultProfile.addEndpoint("","Sts",config.getAliOssStsConfig().getEndpoint());IClientProfileprofile=DefaultProfile.getProfile("",config.getAliOssStsConfig().getAccessKeyId(),config.getAliOssStsConfig().getAccessKeySecret());DefaultAcsClientclient=newDefaultAcsClient(profile);// 构建请求AssumeRoleRequestrequest=newAssumeRoleRequest();request.setSysMethod(MethodType.POST);request.setRoleArn(config.getAliOssStsConfig().getRoleArn());request.setRoleSessionName(config.getAliOssStsConfig().getRoleSessionName());request.setDurationSeconds(config.getAliOssStsConfig().getDurationSeconds());// 调用 APIAssumeRoleResponseresponse=client.getAcsResponse(request);// 构建返回对象AliOssStsDtostsDto=newAliOssStsDto();stsDto.setAccessKeyId(response.getCredentials().getAccessKeyId());stsDto.setAccessKeySecret(response.getCredentials().getAccessKeySecret());stsDto.setSecurityToken(response.getCredentials().getSecurityToken());stsDto.setBucket(config.getAliOssStsConfig().getBucketName());stsDto.setRegion(config.getAliOssStsConfig().getRegionId());stsDto.setOssEndpoint(config.getAliOssConfig().getEndpoint());returnstsDto;}catch(ClientExceptione){LOGGER.error("获取STS凭证失败: {}",e.getErrMsg());returnnull;}}privateAliOssStsDtogetCacheAliOssSts(){returnredisCacheHelper.getCacheObject(AQRedisKeyPrefix.ALI_OSS_STS,AliOssStsDto.class);}privatevoidcacheAliOssSts(AliOssStsDtostsDto){redisCacheHelper.setCacheObject(AQRedisKeyPrefix.ALI_OSS_STS,stsDto,CACHE_TIME,TimeUnit.SECONDS);}}

7.2 客户端实现(JavaScript)

// 1. 获取 STS 凭证asyncfunctiongetStsCredentials(msgType){constresponse=awaitfetch('/api/sts',{method:'POST',body:JSON.stringify({msgType})});returnawaitresponse.json();}// 2. 上传文件asyncfunctionuploadFile(file,msgType){// 获取 STS 凭证conststs=awaitgetStsCredentials(msgType);// 初始化 OSS 客户端constclient=newOSS({accessKeyId:sts.accessKeyId,accessKeySecret:sts.accessKeySecret,stsToken:sts.securityToken,bucket:sts.bucket,region:sts.region,endpoint:sts.endpoint});// 生成文件名constfileName=`${generateUUID()}.${getFileExtension(file.name)}`;constfullPath=`${sts.uploadPath}/${fileName}`;// 上传文件constresult=awaitclient.put(fullPath,file);// 返回文件 URLreturnresult.url;}// 使用示例constfileInput=document.getElementById('fileInput');fileInput.addEventListener('change',async(e)=>{constfile=e.target.files[0];constfileUrl=awaituploadFile(file,'image');console.log('文件上传成功:',fileUrl);});

八、总结

使用阿里云 OSS + STS 实现文件上传方案,具有以下优势:
安全性

  • ✅ 临时凭证,自动过期
  • ✅ 权限最小化,精确控制
  • ✅ 主账号密钥不暴露

性能

  • ✅ 客户端直传,不经过服务端
  • ✅ 凭证缓存,减少 API 调用
  • ✅ 响应速度快,用户体验好

可维护性

  • ✅ 路径组织清晰,便于管理
  • ✅ 日志完整,便于审计
  • ✅ 配置灵活,易于扩展

关键要点

  • 使用 STS 而非永久密钥:保证安全性
  • 实现凭证缓存:提升性能和降低成本
  • 合理组织文件路径:便于管理和权限控制
  • 遵循安全最佳实践:多层防护,降低风险

希望本文能帮助大家更好地理解和实现安全的文件上传方案。在实际项目中,要根据具体业务需求调整配置,但核心安全原则是不变的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/25 0:51:55

饮食饮水代谢检测系统 呼吸能量饮食饮水代谢检测系统 大鼠代谢系统 小鼠代谢系统

动物代谢监测系统具有多通量、实时统计、自动化、高准确性等优点&#xff0c;极大地提高了药物研发和基础生命科学研究的效率&#xff0c;并从根本上减少手工操作带来数据偏差及误差。在动物无拘束状态下&#xff0c;进行多通道测量,记录软件能实时统计大小鼠的饮食量、饮水量、…

作者头像 李华
网站建设 2026/3/20 9:06:24

智能论文改写工具推荐,8款AI平台助你轻松完成写作

目前市面上有多款AI论文辅助工具&#xff0c;通过对8个主流平台的综合评测发现&#xff0c;这些工具在论文降重、降低AI生成内容检测率以及辅助写作等方面各具特色&#xff0c;根据实际测试结果和用户评价显示&#xff0c;其性能表现主要取决于处理效率、内容准确度及操作便捷性…

作者头像 李华
网站建设 2026/3/26 18:36:35

基于SpringBoot+Spark+vue的在线广告推荐系统

前言 在线广告推荐系统的研究与开发从理论上讲&#xff0c;该系统涉及数据挖掘、机器学习、用户行为分析等多个前沿领域&#xff0c;为研究者提供了一个综合性的研究课题。通过不断优化推荐算法&#xff0c;可以推动相关理论的发展&#xff0c;特别是在大数据处理和实时推荐技术…

作者头像 李华
网站建设 2026/3/24 14:21:24

上位机是什么意思?图文并茂的新手教程

上位机是什么&#xff1f;一文搞懂工业控制中的“大脑”角色你有没有在工厂里见过这样的场景&#xff1a;一个操作员坐在电脑前&#xff0c;轻点几下鼠标&#xff0c;整条生产线就开始有序运转&#xff1b;屏幕上跳动着各种曲线、仪表盘和报警信息&#xff0c;仿佛一切尽在掌握…

作者头像 李华
网站建设 2026/3/21 3:40:39

你了解‌板卡控制的架构吗?‌板卡控制和PLC控制有什么区别

‌板卡控制在智能制造、能源管理、医疗研发等领域均有所使用&#xff0c;由此可见‌板卡控制的重要性。为增进大家对‌板卡控制的认识&#xff0c;本文将对‌板卡控制的架构与功能以及‌板卡控制与PLC控制的区别予以介绍。如果你对‌板卡控制具有兴趣&#xff0c;不妨继续往下阅…

作者头像 李华