릴리스 노트
제품 공지
// 임시 키 서비스 예시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/capisecretId: process.env.COS_SECRET_ID,secretKey: process.env.COS_SECRET_KEY,// 키 유효 기간durationSeconds: 1800,// 여기에 버킷 및 리전을 입력합니다. 예: test-1250000000, ap-guangzhoubucket: 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 mainimport ("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 stringappId stringSecretId stringSecretKey stringProxy stringDurationSeconds intBucket stringRegion stringAllowActions []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()) + 1ymd := 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] = nilcontinue}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=1opt := &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.BucketresultMap["region"] = config.RegionresultMap["key"] = keyreturn 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"] = tokendata["authorization"] = signaturedata["cosHost"] = hostdata["cosKey"] = name// JSON으로 변환jsonData, err := json.MarshalIndent(data, "", " ")if err != nil {fmt.Println("JSON 인코딩 실패:", err)return}fmt.Println(string(jsonData))}
<?phprequire_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-8import jsonimport osimport datetimeimport randomfrom urllib.parse import urlencodefrom qcloud_cos import CosConfig, CosS3Clientfrom sts.sts import Stsif __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_keysegments = 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 credentialexcept 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 !");}}/*** 기본 임시 키 신청 예시, 한 버킷 내의 일련의 객체 경로에 대해 일괄적으로 일련의 작업 권한을 부여하는 데 적합함*/@Testpublic 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=1public 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; // 서명에서 검증해야 할 headerpreSignatureStruct.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)}");}}}
피드백