前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JavaScript 结合 Go 实现 临时密钥(STS)

JavaScript 结合 Go 实现 临时密钥(STS)

原创
作者头像
一介程序员
发布2023-08-15 13:41:23
3940
发布2023-08-15 13:41:23
举报
文章被收录于专栏:写两行代码放松下

背景介绍

最近有个项目,需要通过网页上传文件到对象存储中,在查看COS快速入门时,文档建议使用获取临时密钥:

由于固定密钥放在前端会有安全风险,正式部署时我们推荐使用临时密钥的方式,实现过程为:前端首先请求服务端,服务端使用固定密钥调用 STS 服务申请临时密钥(具体内容请参见 临时密钥生成和使用指引 文档),然后返回临时密钥到前端使用。

没想到这个过程一言难尽啊。

开箱即用

先贴代码,以备后用,注意:这里的代码仅适合JavaScript和Go配合,特别是前端代码,和官网例子也是有区别的

后端采用gin框架,这里假设绑定到URL地址为/api/stsr.POST("/sts", tencentSTS)

这段代码授予了临时密钥所有的权限,实际使用时,建议按照最小权限原则进行授权,详细权限可以参考COS API 授权策略使用指引

代码语言:go
复制
package api

import (
	"github.com/gin-gonic/gin"
	"github.com/tencentyun/qcloud-cos-sts-sdk/go"
	"strings"
	"time"
)

type STSRequest struct {
	Region string
	Bucket string
}

func tencentSTS(c *gin.Context) {

	var request STSRequest
	if err := c.ShouldBindJSON(&request); err != nil {
		c.JSON(500, err)
		return
	}
	// 云 API 密钥 SecretId 建议通过环境变量或者本地文件来读取
	secretId := "<SecretId>"
	// 云 API 密钥 SecretKey 建议通过环境变量或者本地文件来读取
	secretKey := "<SecretKey>"
	appid := request.Bucket[strings.LastIndex(request.Bucket, "-")+1:]
	bucket := request.Bucket
	region := request.Region
	client := sts.NewClient(secretId, secretKey, nil)
	// 策略概述 https://cloud.tencent.com/document/product/436/18023
	opt := &sts.CredentialOptions{
		DurationSeconds: int64(time.Hour.Seconds()),
		Region:          "ap-guangzhou",
		Policy: &sts.CredentialPolicy{
			Statement: []sts.CredentialPolicyStatement{
				{
					Action: []string{
						// 所有权限
						"*",
					},
					Effect: "allow",
					Resource: []string{
						//这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径,例子: a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
						//存储桶的命名格式为 BucketName-APPID,此处填写的 bucket 必须为此格式
						"qcs::cos:" + region + ":uid/" + appid + ":" + bucket + "/*",
					},
				},
			},
		},
	}
	if res, err := client.GetCredential(opt); err != nil {
		c.JSON(500, err)
	} else {
		c.JSON(200, res)
	}
}
代码语言:typescript
复制
const cos = new COS({
  getAuthorization: function (options, callback) {
    // 异步获取临时密钥
    // 服务端 JS 和 PHP 例子:https://github.com/tencentyun/cos-js-sdk-v5/blob/master/server/
    // 服务端其他语言参考 COS STS SDK :https://github.com/tencentyun/qcloud-cos-sts-sdk
    // STS 详细文档指引看:https://cloud.tencent.com/document/product/436/14048
    const url = '/api/sts' // url 替换成您自己的后端服务
    const xhr = new XMLHttpRequest()
    xhr.open('POST', url, true)
    xhr.onload = function () {
      try {
        const data = JSON.parse(this.responseText)
        const credentials = data.Credentials
        if (!data || !credentials) {
          return console.error('credentials invalid:\n' + JSON.stringify(data, null, 2))
        }
        callback({
          TmpSecretId: credentials.TmpSecretId,
          TmpSecretKey: credentials.TmpSecretKey,
          SecurityToken: credentials.Token,
          // 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
          StartTime: data.StartTime, // 时间戳,单位秒,如:1580000000
          ExpiredTime: data.ExpiredTime // 时间戳,单位秒,如:1580000000
        })
      } catch (e) {
        console.error('credentials invalid:' + e)
      }
    }
    xhr.send(JSON.stringify(options))
  }
})

开始吐槽

接下来是吐槽时间:

Go SDK中对CredentialResultCredentials的定义如下

代码语言:go
复制
type Credentials struct {
	TmpSecretID  string `json:"TmpSecretId,omitempty"`
	TmpSecretKey string `json:"TmpSecretKey,omitempty"`
	SessionToken string `json:"Token,omitempty"`
}

type CredentialResult struct {
	Credentials *Credentials     `json:"Credentials,omitempty"`
	ExpiredTime int              `json:"ExpiredTime,omitempty"`
	Expiration  string           `json:"Expiration,omitempty"`
	StartTime   int              `json:"StartTime,omitempty"`
	RequestId   string           `json:"RequestId,omitempty"`
	Error       *CredentialError `json:"Error,omitempty"`
}

官网JavaScript代码如下:

代码语言:javascript
复制
callback({
      TmpSecretId: credentials.tmpSecretId,
      TmpSecretKey: credentials.tmpSecretKey,
      SecurityToken: credentials.sessionToken,
      // 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
      StartTime: data.startTime, // 时间戳,单位秒,如:1580000000
      ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000000
});

这里面tmpSecretIdtmpSecretKey等等所有的字段都是小写开头的,但是Go SDK中定义却是大写开头的,更坑的是,sessionToken这个字段在Go里面直接变成了Token。所以前文提供的javascript代码都修复了这些问题。

另外,文档中建议按照最小权限原则进行授权,但是COS API 授权策略使用指引居然没有列出所有的权限,搞得我干脆给了所有权限。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景介绍
  • 开箱即用
  • 开始吐槽
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档