tencent cloud

Cloud Object Storage

동향 및 공지
릴리스 노트
제품 공지
제품 소개
제품 개요
기능 개요
적용 시나리오
제품 장점
기본 개념
리전 및 액세스 도메인
규격 및 제한
제품 요금
과금 개요
과금 방식
과금 항목
프리 티어
과금 예시
청구서 보기 및 다운로드
연체 안내
FAQ
빠른 시작
콘솔 시작하기
COSBrowser 시작하기
사용자 가이드
요청 생성
버킷
객체
데이터 관리
일괄 프로세스
글로벌 가속
모니터링 및 알람
운영 센터
데이터 처리
스마트 툴 박스 사용 가이드
데이터 워크플로
애플리케이션 통합
툴 가이드
툴 개요
환경 설치 및 설정
COSBrowser 툴
COSCLI 툴
COSCMD 툴
COS Migration 툴
FTP Server 툴
Hadoop 툴
COSDistCp 툴
HDFS TO COS 툴
온라인 도구 (Onrain Dogu)
자가 진단 도구
실습 튜토리얼
개요
액세스 제어 및 권한 관리
성능 최적화
AWS S3 SDK를 사용하여 COS에 액세스하기
데이터 재해 복구 백업
도메인 관리 사례
이미지 처리 사례
COS 오디오/비디오 플레이어 사례
데이터 다이렉트 업로드
데이터 보안
데이터 검증
빅 데이터 사례
COS 비용 최적화 솔루션
3rd party 애플리케이션에서 COS 사용
마이그레이션 가이드
로컬 데이터 COS로 마이그레이션
타사 클라우드 스토리지 데이터를 COS로 마이그레이션
URL이 소스 주소인 데이터를 COS로 마이그레이션
COS 간 데이터 마이그레이션
Hadoop 파일 시스템과 COS 간 데이터 마이그레이션
데이터 레이크 스토리지
클라우드 네이티브 데이터 레이크
메타데이터 가속
데이터 레이크 가속기 GooseFS
데이터 처리
데이터 처리 개요
이미지 처리
미디어 처리
콘텐츠 조정
파일 처리
문서 미리보기
장애 처리
RequestId 가져오기
공용 네트워크로 COS에 파일 업로드 시 속도가 느린 문제
COS 액세스 시 403 에러 코드 반환
리소스 액세스 오류
POST Object 자주 발생하는 오류
보안 및 컴플라이언스
데이터 재해 복구
데이터 보안
액세스 관리
자주 묻는 질문
인기 질문
일반 문제
과금
도메인 규정 준수 문제
버킷 설정 문제
도메인 및 CDN 문제
파일 작업 문제
로그 모니터링 문제
권한 관리
데이터 처리 문제
데이터 보안 문제
사전 서명 URL 관련 문제
SDK FAQ
툴 관련 문제
API 관련 문제
Agreements
Service Level Agreement
개인 정보 보호 정책
데이터 처리 및 보안 계약
연락처
용어집

서버 서명 사례

PDF
포커스 모드
폰트 크기
마지막 업데이트 시간: 2025-09-19 10:43:53
본 문서는 안전한 임시 서명 서비스를 구축하는 방법을 설명합니다.

솔루션 장점

권한 보안: 안전한 권한 범위를 효과적으로 제한할 수 있으며, 지정된 하나의 파일 경로에만 업로드할 수 있습니다.
경로 보안: 서버가 무작위 COS 파일 경로를 결정하므로 기존 파일이 덮어쓰여지는 문제와 보안 위험을 효과적으로 방지할 수 있습니다.
전송 보안: 서버에서 서명을 생성하여 임시 키 전송 과정에서 발생할 수 있는 유출 위험을 방지합니다.

업로드 절차

1. 클라이언트에서 파일을 선택하면 클라이언트가 원본 파일 이름을 서버로 전송합니다.
2. 서버는 파일 이름 확장자에 따라 시간이 포함된 임의의 COS 파일 경로를 생성하고, 해당 권한의 서명 정보와 cos key를 요청하여 클라이언트에 반환합니다.

임시 서명 서비스 구축

임시 키(임시 액세스 자격 증명)는 CAM TencentCloud 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 = {
// Tencent Cloud 키를 획득합니다. 제한된 권한을 가진 서브 사용자의 키 사용을 권장합니다. 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}`;

// 1단계: SignKey 생성
var signKey = crypto.createHmac(signAlgorithm, credentials.tmpSecretKey).update(keyTime).digest('hex');
console.log("signKey:"+signKey);

// 2단계: 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);

// 3단계: Signature 생성
var signature = crypto.createHmac(signAlgorithm, signKey).update(stringToSign).digest('hex');
console.log("signature:"+signature);

// 4단계: 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)

// 필드 이름을 LowerCamel로 변환
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
}

// LowerCamel 형식으로 변환(첫 글자가 소문자로)
func toLowerCamel(s string) string {
if s == "" {
return s
}

// 전체 대문자 단어 처리(예: ID)
if strings.ToUpper(s) == s {
return strings.ToLower(s)
}

// 일반 LowerCamel 변환
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
}
// LowerCamelMap으로 변환
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();

// 클라이언트에서 전송된 원본 파일 이름을 기반으로 파일 확장자에 따라 임의의 키 생성
$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자리 난수 생성, 앞에 0으로 채움
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; // 요청 서명 시간은 600초
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)}");
}
}
}




도움말 및 지원

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

피드백