tencent cloud

对象存储

动态与公告
产品动态
产品公告
产品简介
产品概述
功能概览
应用场景
产品优势
基本概念
地域和访问域名
规格与限制
产品计费
计费概述
计费方式
计费项
免费额度
计费示例
查看和下载账单
欠费说明
常见问题
快速入门
控制台快速入门
COSBrowser 快速入门
用户指南
创建请求
存储桶
对象
数据管理
批量处理
全球加速
监控与告警
运维中心
数据处理
内容审核
智能工具箱
数据工作流
应用集成
工具指南
工具概览
环境安装与配置
COSBrowser 工具
COSCLI 工具
COSCMD 工具
COS Migration 工具
FTP Server 工具
Hadoop 工具
COSDistCp 工具
HDFS TO COS 工具
GooseFS-Lite 工具
在线辅助工具
自助诊断工具
实践教程
概览
访问控制与权限管理
性能优化
使用 AWS S3 SDK 访问 COS
数据容灾备份
域名管理实践
图片处理实践
COS 音视频播放器实践
工作流实践
数据直传
内容审核实践
数据安全
数据校验
大数据实践
COS 成本优化解决方案
在第三方应用中使用 COS
迁移指南
本地数据迁移至 COS
第三方云存储数据迁移至 COS
以 URL 作为源地址的数据迁移至 COS
COS 之间数据迁移
Hadoop 文件系统与 COS 之间的数据迁移
数据湖存储
云原生数据湖
元数据加速
数据加速器 GooseFS
数据处理
数据处理概述
图片处理
媒体处理
内容审核
文件处理
文档处理
故障处理
获取 RequestId 操作指引
通过外网上传文件至 COS 缓慢
访问 COS 时返回403错误码
资源访问异常
POST Object 常见异常
API 文档
简介
公共请求头部
公共响应头部
错误码
请求签名
操作列表
Service 接口
Bucket 接口
Object 接口
批量处理接口
数据处理接口
任务与工作流
内容审核接口
云查毒接口
SDK 文档
SDK 概览
准备工作
Android SDK
C SDK
C++ SDK
.NET(C#) SDK
Flutter SDK
Go SDK
iOS SDK
Java SDK
JavaScript SDK
Node.js SDK
PHP SDK
Python SDK
React Native SDK
小程序 SDK
错误码
鸿蒙(Harmony) SDK
终端 SDK 质量优化
安全与合规
数据容灾
数据安全
访问管理
常见问题
热门问题
一般性问题
计费计量问题
域名合规问题
存储桶配置问题
域名和 CDN 问题
文件操作问题
日志监控问题
权限管理问题
数据处理问题
数据安全问题
预签名 URL 问题
SDK 类问题
工具类问题
API 类问题
服务协议
Service Level Agreement
隐私政策
数据处理和安全协议
联系我们
词汇表

服务端签名实践

PDF
포커스 모드
폰트 크기
마지막 업데이트 시간: 2025-09-19 10:43:53
本文档介绍如何搭建安全的临时签名服务。

方案优势

权限安全:可以有效限定安全的权限范围,只能用于上传指定的一个文件路径。
路径安全:由服务端决定随机的 COS 文件路径,可以有效避免已有文件被覆盖的问题和安全风险。
传输安全:在服务端生成签名,避免临时密钥传输过程中泄漏的风险。

上传流程

1. 客户端选择文件,客户端将原始文件名发送给服务端。
2. 服务端根据文件名后缀,生成带时间的随机 COS 文件路径,并申请对应权限的签名信息和 cos key 返回给前端。

临时签名服务搭建

临时密钥(临时访问凭证)是通过 CAM 云 API 提供的接口,获取到权限受限的密钥。当需要发起 COS API 请求时,需要用到获取临时密钥接口返回信息中 的 TmpSecretId、TmpSecretKey 和 Token 三个字段,用于计算签名。
在请求服务端签名时,会先获取临时密钥,然后用临时密钥生成签名,返回客户端。
以下示例各语言示例代码:
Node.js
Go
PHP
Python
Java
.NET(C#)
完整代码可参考 示例代码
// 临时密钥服务例子
var express = require('express');
var crypto = require('crypto');
var moment = require('moment');
var STS = require('qcloud-cos-sts');
const url = require('url');
const { log } = require('console');

// 配置参数
var config = {
// 获取腾讯云密钥,建议使用限定权限的子用户的密钥 https://console.tencentcloud.com/cam/capi
secretId: process.env.COS_SECRET_ID,
secretKey: process.env.COS_SECRET_KEY,
// 密钥有效期
durationSeconds: 1800,
// 这里填写存储桶、地域,例如:test-1250000000、ap-guangzhou
bucket: process.env.PERSIST_BUCKET,
region: process.env.PERSIST_BUCKET_REGION
};

// 获取临时密钥
var getTempCredential = async function(cosKey){
var shortBucketName = config.bucket.substr(0 , config.bucket.lastIndexOf('-'));
var appId = config.bucket.substr(1 + config.bucket.lastIndexOf('-'));
// 开始获取临时密钥
var policy = {
"version": "2.0",
"statement": [
{
"action": [
// 上传需要的操作
"name/cos:PutObject"
],
"effect": "allow",
"resource": [
// 仅限cosKey资源
'qcs::cos:' + config.region + ':uid/' + appId + ':prefix//' + appId + '/' + shortBucketName + '/' + cosKey,
]
}
]
};
let tempKeys = null;
try{
tempKeys = await STS.getCredential({
secretId: config.secretId,
secretKey: config.secretKey,
durationSeconds: config.durationSeconds,
policy: policy,
});
console.log(tempKeys);
return tempKeys;
} catch(err){
console.log(err);
res.send(JSON.stringify(err));
return null;
}
};

// 计算签名
var getSignature = function(tempCredential, httpMethod, cosHost, pathname) {
const signAlgorithm = 'sha1';
const credentials = tempCredential.credentials;
const keyTime = `${tempCredential.startTime};${tempCredential.expiredTime}`;

// 步骤一:生成 SignKey
var signKey = crypto.createHmac(signAlgorithm, credentials.tmpSecretKey).update(keyTime).digest('hex');
console.log("signKey:"+signKey);

// 步骤二:生成 StringToSign
const httpString = `${httpMethod.toLowerCase()}\\n/${pathname}\\n\\nhost=${cosHost}\\n`;
console.log("httpString:"+httpString);
const httpStringHash = crypto.createHash(signAlgorithm).update(httpString).digest('hex');
const stringToSign = `${signAlgorithm}\\n${keyTime}\\n${httpStringHash}\\n`;
console.log("stringToSign:"+stringToSign);

// 步骤三:生成 Signature
var signature = crypto.createHmac(signAlgorithm, signKey).update(stringToSign).digest('hex');
console.log("signature:"+signature);

// 步骤四:生成 authorization
let authorization = `q-sign-algorithm=${signAlgorithm}&
q-ak=${credentials.tmpSecretId}&
q-sign-time=${keyTime}&
q-key-time=${keyTime}&
q-header-list=host&q-url-param-list=&q-signature=${signature}`;

// 去掉掉上面换行导致的\\n
authorization = authorization.replace(/\\n/g, '');
console.log("authorization:"+authorization);
return authorization;
}

// 创建临时密钥服务和用于调试的静态服务
var app = express();
// 直传签名接口
app.all('/sts-server-sign', async function (req, res, next) {
// 获取需要签名的字段
var httpMethod = req.query.httpMethod;
var cosHost = req.query.host;
var cosKey = req.query.cosKey;

console.log(httpMethod + " " + cosHost + " " + cosKey);

cosHost = decodeURIComponent(cosHost);
cosKey = decodeURIComponent(cosKey);

console.log(httpMethod + " " + cosHost + " " + cosKey);

// 判断异常情况
if (!config.secretId || !config.secretKey) return res.send({ code: '-1', message: 'secretId or secretKey not ready' });
if (!config.bucket || !config.region) return res.send({ code: '-1', message: 'bucket or regions not ready' });
if (!httpMethod || !cosHost || !cosKey) return res.send({ code: '-1', message: 'httpMethod or host or coskey is not empty' });

// 开始获取临时密钥
var tempCredential = await getTempCredential(cosKey);
if(!tempCredential){
res.send({ code: -1, message: 'get temp credentials fail' });
return;
}

// 用临时密钥计算签名
let authorization = getSignature(tempCredential, httpMethod, cosHost, cosKey);

// 返回域名、文件路径、签名、凭证信息
res.send({
code: 0,
data: {
cosHost: cosHost,
cosKey: cosKey,
authorization: authorization,
securityToken: tempCredential.credentials.sessionToken
},
});
});

app.all('*', function (req, res, next) {
res.send({ code: -1, message: '404 Not Found' });
});

// 启动签名服务
app.listen(3000);
console.log('app is listening at http://127.0.0.1:3000');

完整代码可参考 示例代码
package main

import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/tencentyun/cos-go-sdk-v5"
sts "github.com/tencentyun/qcloud-cos-sts-sdk/go"
"math/rand"
"net/http"
"net/url"
"os"
"reflect"
"strings"
"time"
"unicode"
)

type Config struct {
filename string
appId string
SecretId string
SecretKey string
Proxy string
DurationSeconds int
Bucket string
Region string
AllowActions []string
}

type Permission struct {
LimitExt bool `json:"limitExt"`
ExtWhiteList []string `json:"extWhiteList"`
LimitContentType bool `json:"limitContentType"`
LimitContentLength bool `json:"limitContentLength"`
}

func generateCosKey(ext string) string {
date := time.Now()
m := int(date.Month()) + 1
ymd := fmt.Sprintf("%d%02d%d", date.Year(), m, date.Day())
r := fmt.Sprintf("%06d", rand.Intn(1000000))
cosKey := fmt.Sprintf("file/%s/%s_%s.%s", ymd, ymd, r, ext)
return cosKey
}

func getPermission() Permission {
permission := Permission{
LimitExt: true,
ExtWhiteList: []string{"jpg", "jpeg", "png", "gif", "bmp"},
LimitContentType: false,
LimitContentLength: false,
}
return permission
}

func getConfig() Config {
config := Config{
filename: "test.jpg",
appId: "12500000000",
SecretId: os.Getenv("SECRETID"), // 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://www.tencentcloud.com/document/product/598/37140?from_cn_redirect=1
SecretKey: os.Getenv("SECRETKEY"), // 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://www.tencentcloud.com/document/product/598/37140?from_cn_redirect=1
Proxy: os.Getenv("Proxy"),
DurationSeconds: 1800,
Bucket: "0-1253960454",
Region: "ap-guangzhou",
AllowActions: []string{
"name/cos:PutObject",
},
}
return config
}

func stringInSlice(str string, list []string) bool {
for _, v := range list {
if v == str {
return true
}
}
return false
}

func StructToCamelMap(input interface{}) map[string]interface{} {
v := reflect.ValueOf(input)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}

result := make(map[string]interface{})
typ := v.Type()

for i := 0; i < v.NumField(); i++ {
field := typ.Field(i)
fieldValue := v.Field(i)

// 转换字段名为小驼峰
key := toLowerCamel(field.Name)

// 处理嵌套结构体
if fieldValue.Kind() == reflect.Struct ||
(fieldValue.Kind() == reflect.Ptr && fieldValue.Elem().Kind() == reflect.Struct) {

if fieldValue.IsNil() && fieldValue.Kind() == reflect.Ptr {
result[key] = nil
continue
}

result[key] = StructToCamelMap(fieldValue.Interface())
} else {
// 处理基本类型
result[key] = fieldValue.Interface()
}
}

return result
}

// 转换为小驼峰格式(首字母小写)
func toLowerCamel(s string) string {
if s == "" {
return s
}

// 处理全大写单词(如 ID)
if strings.ToUpper(s) == s {
return strings.ToLower(s)
}

// 普通小驼峰转换
runes := []rune(s)
runes[0] = unicode.ToLower(runes[0])
return string(runes)
}

func getStsCredential() (map[string]interface{}, error) {
config := getConfig()

permission := getPermission()

c := sts.NewClient(
// 通过环境变量获取密钥, os.Getenv 方法表示获取环境变量
config.SecretId, //os.Getenv("SECRETID"), // 用户的 SecretId,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://www.tencentcloud.com/document/product/598/37140?from_cn_redirect=1
config.SecretKey, //os.Getenv("SECRETKEY"), // 用户的 SecretKey,建议使用子账号密钥,授权遵循最小权限指引,降低使用风险。子账号密钥获取可参考https://www.tencentcloud.com/document/product/598/37140?from_cn_redirect=1
nil,
// sts.Host("sts.internal.tencentcloudapi.com"), // 设置域名, 默认域名sts.tencentcloudapi.com
// sts.Scheme("http"), // 设置协议, 默认为https,公有云sts获取临时密钥不允许走http,特殊场景才需要设置http
)

condition := make(map[string]map[string]interface{})

segments := strings.Split(config.filename, ".")
if len(segments) == 0 {
//ext := ""
}
ext := segments[len(segments)-1]

if permission.LimitExt {
extInvalid := ext == "" || !stringInSlice(ext, permission.ExtWhiteList)
if extInvalid {
return nil, errors.New("非法文件,禁止上传")
}
}

if permission.LimitContentType {
condition["string_like_if_exist"] = map[string]interface{}{
// 只允许上传 content-type 为图片类型
"cos:content-type": "image/*",
}
}

// 3. 限制上传文件大小
if permission.LimitContentLength {
condition["numeric_less_than_equal"] = map[string]interface{}{
// 上传大小限制不能超过 5MB(只对简单上传生效)
"cos:content-length": 5 * 1024 * 1024,
}
}

key := generateCosKey(ext)
// 策略概述 https://www.tencentcloud.com/document/product/436/18023?from_cn_redirect=1
opt := &sts.CredentialOptions{
DurationSeconds: int64(config.DurationSeconds),
Region: config.Region,
Policy: &sts.CredentialPolicy{
Version: "2.0",
Statement: []sts.CredentialPolicyStatement{
{
// 密钥的权限列表。简单上传和分片需要以下的权限,其他权限列表请看 https://www.tencentcloud.com/document/product/436/31923?from_cn_redirect=1
Action: config.AllowActions,
Effect: "allow",
Resource: []string{
// 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径,例子: a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
// 存储桶的命名格式为 BucketName-APPID,此处填写的 bucket 必须为此格式
"qcs::cos:ap-guangzhou:uid/" + config.appId + ":" + config.Bucket + "/" + key,
},
// 开始构建生效条件 condition
// 关于 condition 的详细设置规则和COS支持的condition类型可以参考https://www.tencentcloud.com/document/product/436/71306?from_cn_redirect=1
Condition: condition,
},
},
},
}

// case 1 请求临时密钥
res, err := c.GetCredential(opt)
if err != nil {
return nil, err
}
// 转换为小驼峰 map
resultMap := StructToCamelMap(res)
resultMap["bucket"] = config.Bucket
resultMap["region"] = config.Region
resultMap["key"] = key

return resultMap, nil
}

func main() {
result, err := getStsCredential()
if err != nil {
fmt.Printf("请求临时密钥失败: %v\\n", err)
return // 根据上下文决定是否退出
}

// 类型断言 credentials 为 map[string]interface{}
credentials, ok := result["credentials"].(map[string]interface{})
if !ok {
fmt.Println("凭证格式错误")
return
}
// 带类型检查的字段获取
tak, tok := credentials["tmpSecretID"].(string)
tsk, tskok := credentials["tmpSecretKey"].(string)
token, tokenok := credentials["sessionToken"].(string)
if !tok || !tskok || !tokenok {
fmt.Println("临时凭证字段缺失或类型错误")
return
}
host := "https://" + result["bucket"].(string) + ".cos." + result["region"].(string) + ".myqcloud.com"
u, _ := url.Parse(host)
b := &cos.BaseURL{BucketURL: u}
c := cos.NewClient(b, &http.Client{})
name := result["key"].(string)
ctx := context.Background()
opt := &cos.PresignedURLOptions{
Query: &url.Values{},
Header: &http.Header{},
}
opt.Query.Add("x-cos-security-token", token)
signature := c.Object.GetSignature(ctx, http.MethodPut, name, "tak", "tsk", time.Hour, opt, true)
fmt.Printf("%s\\n%s\\n%s\\n%s\\n", tak, tsk, token, signature)
data := make(map[string]string)

// 添加token和sign
data["securityToken"] = token
data["authorization"] = signature
data["cosHost"] = host
data["cosKey"] = name
// 转换为JSON
jsonData, err := json.MarshalIndent(data, "", " ")
if err != nil {
fmt.Println("JSON编码失败:", err)
return
}
fmt.Println(string(jsonData))
}


完整代码可参考 示例代码
<?php
require_once __DIR__ . '/vendor/autoload.php';

use QCloud\\COSSTS\\Sts;

// 生成要上传的 COS 文件路径文件名
function generateCosKey($ext) {
$ymd = date('Ymd');
$r = substr('000000' . rand(), -6);
$cosKey = 'file/' . $ymd. '/' . $ymd . '_' . $r;
if ($ext) {
$cosKey = $cosKey . '.' . $ext;
}
return $cosKey;
};

// 获取单一文件上传权限的临时密钥
function getKeyAndCredentials($filename) {
// 业务自行实现 用户登录态校验,比如对 token 校验
// $canUpload = checkUserRole($userToken);
// if (!$canUpload) {
// return '当前用户没有上传权限';
// }

// 上传文件可控制类型、大小,按需开启
$permission = array(
'limitExt' => false, // 限制上传文件后缀
'extWhiteList' => ['jpg', 'jpeg', 'png', 'gif', 'bmp'], // 限制的上传后缀
'limitContentType' => false, // 限制上传 contentType
'limitContentLength' => false, // 限制上传文件大小
);
$condition = array();

// 客户端传进原始文件名,这里根据文件后缀生成随机 Key
$ext = pathinfo($filename, PATHINFO_EXTENSION);

// 1. 限制上传文件后缀
if ($permission['limitExt']) {
if ($ext === '' || array_key_exists($ext, $permission['extWhiteList'])) {
return '非法文件,禁止上传';
}
}

// 2. 限制上传文件 content-type
if ($permission['limitContentType']) {
// 只允许上传 content-type 为图片类型
$condition['string_like_if_exist'] = array('cos:content-type' => 'image/*');
}

// 3. 限制上传文件大小
if ($permission['limitContentLength']) {
// 上传大小限制不能超过 5MB(只对简单上传生效)
$condition['numeric_less_than_equal'] = array('cos:content-length' => 5 * 1024 * 1024);
}

$cosKey = generateCosKey($ext);
$bucket = 'test-125000000'; // 换成你的 bucket
$region = 'ap-guangzhou'; // 换成 bucket 所在园区
$config = array(
'url' => 'https://sts.tencentcloudapi.com/', // url和domain保持一致
'domain' => 'sts.tencentcloudapi.com', // 域名,非必须,默认为 sts.tencentcloudapi.com
'proxy' => '',
'secretId' => "",//getenv('GROUP_SECRET_ID'), // 固定密钥,若为明文密钥,请直接以'xxx'形式填入,不要填写到getenv()函数中
'secretKey' => "",//getenv('GROUP_SECRET_KEY'), // 固定密钥,若为明文密钥,请直接以'xxx'形式填入,不要填写到getenv()函数中
'bucket' => $bucket, // 换成你的 bucket
'region' => $region, // 换成 bucket 所在园区
'durationSeconds' => 1800, // 密钥有效期
'allowPrefix' => array($cosKey), // 只分配当前 key 的路径权限
// 密钥的权限列表。简单上传和分片需要以下的权限,其他权限列表请看 https://www.tencentcloud.com/document/product/436/31923?from_cn_redirect=1
'allowActions' => array (
// 简单上传
'name/cos:PutObject'
),
);

if (!empty($condition)) {
$config['condition'] = $condition;
}

$sts = new Sts();
$tempKeys = $sts->getTempKeys($config);
$resTemp = array_merge(
$tempKeys,
[
'startTime' => time(),
'bucket' => $bucket,
'region' => $region,
'key' => $cosKey,
]
);
return $resTemp;
}

function getSign()
{
$filename = "test.jpg";
$method = "putObject";
$result = getKeyAndCredentials($filename);
$credentials = $result["credentials"];
$sessionToken = $credentials["sessionToken"];
$tmpSecretId = $credentials["tmpSecretId"];
$tmpSecretKey = $credentials["tmpSecretKey"];
$expiredTime = $result["expiredTime"];
$startTime = $result["startTime"];
$bucket = $result["bucket"];
$region = $result["region"];
$key = $result["key"];

$cosClient = new Qcloud\\Cos\\Client(
array(
'region' => $region,
'scheme' => 'https', //协议头部,默认为 http
'signHost' => true, //默认签入Header Host;您也可以选择不签入Header Host,但可能导致请求失败或安全漏洞,若不签入host则填false
'credentials'=> array(
'secretId' => $tmpSecretId,
'secretKey' => $tmpSecretKey,
'token' => $sessionToken)));
### 简单上传预签名
try {
$url = $cosClient->getPresignedUrl($method,array(
'Bucket' => $bucket,
'Key' => $key,
'Body' => "",
'Params'=> array('x-cos-security-token' => $sessionToken),
'Headers'=> array(),
), $expiredTime - $startTime); //签名的有效时间

$parsedUrl = parse_url($url);
$host = 'https://' . $parsedUrl['host']; // 自动包含端口(如果有)
$queryString = isset($parsedUrl['query']) ? $parsedUrl['query'] : '';
$queryParts = explode('&', $queryString);
$signParts = array_filter($queryParts, function($part) {
return strpos($part, 'x-cos-security-token=') !== 0;
});
$sign = implode('&', $signParts);
$result = [
'cosHost' => $host,
'cosKey' => $key,
'authorization' => $sign,
'securityToken' => $sessionToken
];

echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

} catch (\\Exception $e) {
// 请求失败
echo($e);
}
}
getSign();
完整代码可参考 示例代码
#!/usr/bin/env python
# coding=utf-8
import json
import os
import datetime
import random
from urllib.parse import urlencode

from qcloud_cos import CosConfig, CosS3Client

from sts.sts import Sts


if __name__ == '__main__':

# 配置参数
config = {
"filename":"test.jpg",
"appId": "1250000000",
"secretId": os.getenv("SecretId"),
"secretKey": os.getenv("SecretKey"),
"proxy": os.getenv("Proxy"),
"durationSeconds": 1800,
"bucket": "0-1253960454",
"region": "ap-guangzhou",
# 密钥的上传操作权限列表
"allowActions": [
# 简单上传
"name/cos:PutObject"
],
}

permission = {
"limitExt": True, # 限制上传文件后缀
"extWhiteList": ["jpg", "jpeg", "png", "gif", "bmp"], # 限制的上传后缀
"limitContentType": False, # 限制上传 contentType
"limitContentLength": False, # 限制上传文件大小
}


# 生成要上传的 COS 文件路径文件名
def generate_cos_key(ext=None):
date = datetime.datetime.now()
ymd = date.strftime('%Y%m%d')
r = str(int(random.random() * 1000000)).zfill(6)
cos_key = f"file/{ymd}/{ymd}_{r}.{ext if ext else ''}"
return cos_key


segments = config['filename'].split(".")
ext = segments[-1] if segments else ""
key = generate_cos_key(ext)
resource = f"qcs::cos:{config['region']}:uid/{config['appId']}:{config['bucket']}/{key}"

condition = {}

# 1. 限制上传文件后缀
if permission["limitExt"]:
ext_invalid = not ext or ext not in permission["extWhiteList"]
if ext_invalid:
print('非法文件,禁止上传')

# 2. 限制上传文件 content-type
if permission["limitContentType"]:
condition.update({
"string_like_if_exist": {
# 只允许上传 content-type 为图片类型
"cos:content-type": "image/*"
}
})

# 3. 限制上传文件大小
if permission["limitContentLength"]:
condition.update({
"numeric_less_than_equal": {
# 上传大小限制不能超过 5MB(只对简单上传生效)
"cos:content-length": 5 * 1024 * 1024
}
})


def get_credential_demo():
credentialOption = {
# 临时密钥有效时长,单位是秒
'duration_seconds': config.get('durationSeconds'),
'secret_id': config.get("secretId"),
# 固定密钥
'secret_key': config.get("secretKey"),
# 换成你的 bucket
'bucket': config.get("bucket"),
'proxy': config.get("proxy"),
# 换成 bucket 所在地区
'region': config.get("region"),
"policy": {
"version": '2.0',
"statement": [
{
"action": config.get("allowActions"),
"effect": "allow",
"resource": [
resource
],
"condition": condition
}
],
},
}

try:

sts = Sts(credentialOption)
response = sts.get_credential()
credential_dic = dict(response)
credential_info = credential_dic.get("credentials")
credential = {
"bucket": config.get("bucket"),
"region": config.get("region"),
"key": key,
"startTime": credential_dic.get("startTime"),
"expiredTime": credential_dic.get("expiredTime"),
"requestId": credential_dic.get("requestId"),
"expiration": credential_dic.get("expiration"),
"credentials": {
"tmpSecretId": credential_info.get("tmpSecretId"),
"tmpSecretKey": credential_info.get("tmpSecretKey"),
"sessionToken": credential_info.get("sessionToken"),
},
}
return credential
except Exception as e:
print(e)


result = get_credential_demo()
credentials = result["credentials"]
secret_id = credentials["tmpSecretId"]
secret_key = credentials["tmpSecretKey"]
token = credentials["sessionToken"]
bucket = result["bucket"]
region = result["region"]
key = result["key"]
expired = result["expiredTime"]

config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key, Token=token)
client = CosS3Client(config)
sign = client.get_auth(Method='put',Bucket=bucket, Key=key, Expired=expired, Params={
'x-cos-security-token': token
},SignHost=True)
sign = urlencode(dict([item.split('=', 1) for item in sign.split('&')]))
host = "https://" + result["bucket"] + ".cos." + result["region"] + ".myqcloud.com"
response = {
"cosHost":host,
"cosKey":key,
"authorization":sign,
"securityToken":token
}
print('get data : ' + json.dumps(response, indent=4))
完整代码可参考 示例代码
package com.tencent.cloud;

import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicSessionCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpMethodName;
import com.qcloud.cos.region.Region;
import com.tencent.cloud.cos.util.Jackson;
import org.junit.Test;

import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

public class ServerSignTest {

public static String generateCosKey(String ext) {
Date date = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
String ymd = dateFormat.format(date);

Random random = new Random();
int r = random.nextInt(1000000);
String rStr = String.format("%06d", r);

String cosKey = String.format("file/%s/%s_%s.%s", ymd, ymd, rStr, ext != null ? ext : "");
return cosKey;
}

// 获取配置信息
public TreeMap<String,Object> getConfig(){

String bucket = "0-1250000000";
String appId = "1250000000";
String filename = "test.jpg";
String region = "ap-guangzhou";
String secretId = "";
String secretKey = "";
String proxy = "";
int durationSeconds = 1800;

String[] segments = filename.split("\\\\.");
String ext = segments.length > 0 ? segments[segments.length - 1] : "";

// 临时密钥限制
Boolean limitExt = false; // 限制上传文件后缀
List extWhiteList = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp"); // 限制的上传后缀
Boolean limitContentType = false; // 限制上传 contentType
Boolean limitContentLength = false; // 限制上传文件大小


Map<String, Object> condition = new HashMap();

// 1. 限制上传文件后缀
if (limitExt) {
boolean extInvalid = ext == null || !extWhiteList.contains(ext);
if (extInvalid) {
System.out.println("非法文件,禁止上传");
return null;
}
}

// 2. 限制上传文件 content-type
if (limitContentType) {
condition.put("string_like_if_exist", new HashMap<String, String>() {{
put("cos:content-type", "image/*");
}});
}

// 3. 限制上传文件大小(只对简单上传生效)
if (limitContentLength) {
condition.put("numeric_less_than_equal", new HashMap<String, Long>() {{
put("cos:content-length", 5L * 1024 * 1024);
}});
}
String key = generateCosKey(ext);
String resource = "qcs::cos:" + region + ":uid/" + appId + ':' + bucket + '/' + key;
List allowActions = Arrays.asList(
// 简单上传
"name/cos:PutObject"
);

// 构建policy
Map<String, Object> policy = new HashMap();
policy.put("version", "2.0");
Map<String, Object> statement = new HashMap();
statement.put("action", allowActions);
statement.put("effect", "allow");
List<String> resources = Arrays.asList(
resource
);
statement.put("resource", resources);
statement.put("condition", condition);
policy.put("statement", Arrays.asList(statement));


// 构建config
TreeMap <String,Object> config = new TreeMap<String, Object>();
config.put("secretId",secretId);
config.put("secretKey",secretKey);
config.put("proxy",proxy);
config.put("duration",durationSeconds);
config.put("bucket",bucket);
config.put("region",region);
config.put("key",key);
config.put("policy",Jackson.toJsonPrettyString(policy));
return config;
}

public TreeMap <String,Object> getKeyAndCredentials() {
TreeMap config = this.getConfig();
try {
Response response = CosStsClient.getCredential(config);
TreeMap <String,Object> credential = new TreeMap<String, Object>();
TreeMap <String,Object> credentials = new TreeMap<String, Object>();
credentials.put("tmpSecretId",response.credentials.tmpSecretId);
credentials.put("tmpSecretKey",response.credentials.tmpSecretKey);
credentials.put("sessionToken",response.credentials.sessionToken);
credential.put("startTime",response.startTime);
credential.put("expiredTime",response.expiredTime);
credential.put("requestId",response.requestId);
credential.put("expiration",response.expiration);
credential.put("credentials",credentials);
credential.put("bucket",config.get("bucket"));
credential.put("region",config.get("region"));
credential.put("key",config.get("key"));
return credential;
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException("no valid secret !");
}
}
/**
* 基本的临时密钥申请示例,适合对一个桶内的一批对象路径,统一授予一批操作权限
*/
@Test
public void testGetKeyAndCredentials() {
TreeMap <String,Object> credential = this.getKeyAndCredentials();
TreeMap <String,Object> credentials = (TreeMap<String, Object>) credential.get("credentials");
try {


String tmpSecretId = (String) credentials.get("tmpSecretId");
String tmpSecretKey = (String) credentials.get("tmpSecretKey");
String sessionToken = (String) credentials.get("sessionToken");
Date expiredTime = new Date((Long) credential.get("expiredTime"));
String key = (String) credential.get("key");
String bucket = (String) credential.get("bucket");
String region = (String) credential.get("region");

COSCredentials cred = new BasicSessionCredentials(tmpSecretId, tmpSecretKey, sessionToken);
ClientConfig clientConfig = new ClientConfig();
clientConfig.setRegion(new Region(region));

COSClient cosClient = new COSClient(cred, clientConfig);


Map<String, String> headers = new HashMap<String,String>();
Map<String, String> params = new HashMap<String,String>();
params.put("x-cos-security-token",sessionToken);
URL url = cosClient.generatePresignedUrl(bucket, key, expiredTime, HttpMethodName.PUT, headers, params);
String host = "https://" + url.getHost();
String query = url.toString().split("\\\\?")[1];
String sign = query.split("&x-cos-security-token")[0];
TreeMap <String,Object> result = new TreeMap<String, Object>();
result.put("cosHost",host);
result.put("cosKey",key);
result.put("authorization",sign);
result.put("securityToken",sessionToken);
System.out.println(Jackson.toJsonPrettyString(result));
} catch (Exception e) {
e.printStackTrace();
throw new IllegalArgumentException("no valid sign !");
}
}
}
完整代码可参考 示例代码

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Mail;
using COSSTS;
using COSXML;
using COSXML.Auth;
using COSXML.Model.Tag;
using Newtonsoft.Json;
using Formatting = System.Xml.Formatting;


namespace COSSnippet
{
public class ServerSign
{
//永久密钥
string secretId = "";
string secretKey = "";

string bucket = "bucket-1250000000";
string appId = "1250000000";
string region = "ap-guangzhou";
string filename = "test.jpg";
string method = "put";
int time = 1800;
// 限制
Boolean limitExt = false; // 限制上传文件后缀
List<string> extWhiteList = new List<String> { "jpg", "jpeg", "png", "gif", "bmp" }; // 限制的上传后缀
Boolean limitContentType = false; // 限制上传 contentType
Boolean limitContentLength = false; // 限制上传文件大小

public string generateCosKey(string ext)
{
DateTime date = DateTime.Now;
int m = date.Month;
string ymd = $"{date.Year}{(m < 10 ? $"0{m}" : m.ToString())}{date.Day}";
Random random = new Random();
string r = random.Next(0, 1000000).ToString("D6"); // 生成6位随机数,前面补零
string cosKey = $"file/{ymd}/{ymd}_{r}.{(string.IsNullOrEmpty(ext) ? "" : ext)}";
return cosKey;
}
public Dictionary<string, object> getConfig()
{
Dictionary<string, object> config = new Dictionary<string, object>();
string[] allowActions = new string[] { // 允许的操作范围,这里以上传操作为例
"name/cos:PutObject"
};
string[] segments = filename.Split(".");
string ext = segments.Length > 0 ? segments[segments.Length - 1] : string.Empty;
string key = generateCosKey(ext);
string resource = $"qcs::cos:{region}:uid/{appId}:{bucket}/{key}";

var condition = new Dictionary<string, object>();
// 1. 限制上传文件后缀
if (limitExt)
{
var extInvalid = string.IsNullOrEmpty(ext) || !extWhiteList.Contains(ext);
if (extInvalid)
{
Console.WriteLine("非法文件,禁止上传");
return null;
}
}

// 2. 限制上传文件 content-type
if (limitContentType)
{
condition["string_like_if_exist"] = new Dictionary<string, string>
{
{ "cos:content-type", "image/*" } // 只允许上传 content-type 为图片类型
};
}

// 3. 限制上传文件大小(只对简单上传生效)
if (limitContentLength)
{
condition["numeric_less_than_equal"] = new Dictionary<string, long>
{
{ "cos:content-length", 5 * 1024 * 1024 } // 上传大小限制不能超过 5MB
};
}

var policy = new Dictionary<string, object>
{
{ "version", "2.0" },
{ "statement", new List<Dictionary<string, object>>
{
new Dictionary<string, object>
{
{ "action", allowActions },
{ "effect", "allow" },
{ "resource", new List<string>
{
resource,
}
},
{ "condition", condition }
}
}
}
};

// 序列化为 JSON 并输出
string jsonPolicy = JsonConvert.SerializeObject(policy);
config.Add("bucket", bucket);
config.Add("region", region);
config.Add("durationSeconds", time);

config.Add("secretId", secretId);
config.Add("secretKey", secretKey);
config.Add("key", key);
config.Add("policy", jsonPolicy);
return config;
}
// 获取联合身份临时访问凭证 https://www.tencentcloud.com/document/product/1312/48195?from_cn_redirect=1
public Dictionary<string, object> GetCredential()
{

var config = getConfig();
//获取临时密钥
Dictionary<string, object> credential = STSClient.genCredential(config);
Dictionary<string, object> credentials = JsonConvert.DeserializeObject<Dictionary<string, object>>(JsonConvert.SerializeObject((object) credential["Credentials"]));
Dictionary<string, object> credentials1 = new Dictionary<string, object>();
credentials1.Add("tmpSecretId",credentials["TmpSecretId"]);
credentials1.Add("tmpSecretKey",credentials["TmpSecretKey"]);
credentials1.Add("sessionToken",credentials["Token"]);
Dictionary<string, object> dictionary1 = new Dictionary<string, object>();
dictionary1.Add("credentials",credentials1);
dictionary1.Add("startTime",credential["StartTime"]);
dictionary1.Add("requestId",credential["RequestId"]);
dictionary1.Add("expiration",credential["Expiration"]);
dictionary1.Add("expiredTime",credential["ExpiredTime"]);
dictionary1.Add("bucket",config["bucket"]);
dictionary1.Add("region",config["region"]);
dictionary1.Add("key",config["key"]);
return dictionary1;
}
static void Main(string[] args)
{
ServerSign m = new ServerSign();
Dictionary<string, object> result = m.GetCredential();
Dictionary<string, object> credentials = (Dictionary<string, object>)result["credentials"];
string tmpSecretId = (string)credentials["tmpSecretId"];
string tmpSecretKey = (string)credentials["tmpSecretKey"];
string sessionToken = (string)credentials["sessionToken"];
string bucket = (string)result["bucket"];
string region = (string)result["region"];
string key = (string)result["key"];
long expiredTime = (long)result["expiredTime"];
int startTime = (int)result["startTime"];
QCloudCredentialProvider cosCredentialProvider = new DefaultSessionQCloudCredentialProvider(
tmpSecretId, tmpSecretKey, expiredTime, sessionToken);
CosXmlConfig config = new CosXmlConfig.Builder()
.IsHttps(true) //设置默认 HTTPS 请求
.SetRegion(region) //设置一个默认的存储桶地域
.SetDebugLog(true) //显示日志
.Build(); //创建 CosXmlConfig 对象
CosXml cosXml = new CosXmlServer(config, cosCredentialProvider);
PreSignatureStruct preSignatureStruct = new PreSignatureStruct();
preSignatureStruct.appid = m.appId;//"1250000000";
preSignatureStruct.region = region;//"COS_REGION";
preSignatureStruct.bucket = bucket;//"examplebucket-1250000000";
preSignatureStruct.key = "exampleObject"; //对象键
preSignatureStruct.httpMethod = "PUT"; //HTTP 请求方法
preSignatureStruct.isHttps = true; //生成 HTTPS 请求 URL
preSignatureStruct.signDurationSecond = 600; //请求签名时间为 600s
preSignatureStruct.headers = null; //签名中需要校验的 header
preSignatureStruct.queryParameters = null; //签名中需要校验的 URL 中请求参数
//上传预签名 URL (使用永久密钥方式计算的签名 URL)
Dictionary<string, string> queryParameters = new Dictionary<string, string>();
queryParameters.Add("x-cos-security-token",sessionToken);
Dictionary<string, string> headers = new Dictionary<string, string>();
string authorization = cosXml.GenerateSign(m.method,key,queryParameters,headers,expiredTime - startTime,expiredTime - startTime);

string host = "https://" + bucket + ".cos." + region + ".myqcloud.com";
Dictionary<string, object> response = new Dictionary<string, object>();
response.Add("cosHost",host);
response.Add("cosKey",key);
response.Add("authorization",authorization);
response.Add("securityToken",sessionToken);
Console.WriteLine($"{JsonConvert.SerializeObject(response)}");
}
}
}




도움말 및 지원

문제 해결에 도움이 되었나요?

피드백