github.com/SupenBysz/gf-admin-community@v0.7.4/internal/logic/sdk_tencent/sdk_tencent.go (about) 1 package sdk_tencent 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/hmac" 7 "crypto/sha256" 8 "encoding/hex" 9 "fmt" 10 "github.com/SupenBysz/gf-admin-community/sys_model" 11 "github.com/SupenBysz/gf-admin-community/sys_model/sys_dao" 12 "github.com/SupenBysz/gf-admin-community/sys_service" 13 "github.com/gogf/gf/v2/container/garray" 14 "github.com/gogf/gf/v2/database/gdb" 15 "github.com/gogf/gf/v2/encoding/gjson" 16 "github.com/gogf/gf/v2/errors/gerror" 17 "github.com/gogf/gf/v2/frame/g" 18 "github.com/gogf/gf/v2/os/gtime" 19 "github.com/gogf/gf/v2/util/gconv" 20 "github.com/kysion/base-library/utility/json" 21 "github.com/kysion/base-library/utility/kconv" 22 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" 23 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" 24 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" 25 faceid "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/faceid/v20180301" 26 "log" 27 "net/http" 28 "strconv" 29 "strings" 30 "time" 31 ) 32 33 // 腾讯云服务平台 34 35 type sSdkTencent struct { 36 TencentSdkConfTokenList []*sys_model.TencentSdkConfToken 37 sysConfigName string 38 conf gdb.CacheOption 39 } 40 41 // New SdkBaidu 系统配置逻辑实现 42 func New() sys_service.ISdkTencent { 43 return &sSdkTencent{ 44 TencentSdkConfTokenList: make([]*sys_model.TencentSdkConfToken, 0), 45 sysConfigName: "tencent_sdk_conf", 46 conf: gdb.CacheOption{ 47 Duration: time.Hour, 48 Force: false, 49 }, 50 } 51 } 52 53 func init() { 54 sys_service.RegisterSdkTencent(New()) 55 } 56 57 // fetchTencentSdkConfToken 根据 identifier 获取腾讯云API Token (API获取方式) 58 func (s *sSdkTencent) fetchTencentSdkConfToken(ctx context.Context, identifier string) (tokenInfo *sys_model.TencentSdkConfToken, err error) { 59 60 info, err := s.GetTencentSdkConf(ctx, identifier) 61 if err != nil { 62 return nil, err 63 } 64 client := g.Client() 65 66 // URL 请求的服务器URL 67 var host = "https://rkp.tencentcloudapi.com" 68 69 // 请求头 70 header := make(map[string]string) 71 72 header["X-TC-Action"] = "GetToken" 73 header["Content-type"] = "application/json" 74 header["X-TC-Region"] = "" 75 header["X-TC-Timestamp"] = gtime.Now().TimestampStr() 76 header["X-TC-Version"] = info.Version 77 // header["Authorization"] = "" 78 header["X-TC-Language"] = "zh-CN" 79 80 client.Header(header) 81 82 // 请求数据, 83 param := g.Map{ 84 // 业务ID 85 "BusinessId": gconv.Int64(info.AppID), 86 // 业务子场景 87 "Scene": 0, 88 // 业务侧账号体系下的用户ID (不是必填) 89 "BusinessUserId": info.AESKey, 90 // 用户侧的IP (不是必填) 91 "AppClientIp": info.AppID, 92 // 过期时间 (不是必填) 93 "ExpireTime": info.APIKey, 94 } 95 96 response, err := client.Post(ctx, host, param) 97 if err != nil { 98 return nil, sys_service.SysLogs().ErrorSimple(ctx, err, "获取腾讯云API Token 失败", sys_dao.SysConfig.Table()+":"+s.sysConfigName) 99 } 100 101 // 接受返回数据,json解析 102 newTokenInfo := sys_model.TencentAccessToken{} 103 104 err = gjson.DecodeTo(response.ReadAllString(), &newTokenInfo) 105 if nil != err { 106 return nil, sys_service.SysLogs().ErrorSimple(ctx, err, "获取腾讯云API Token 失败", sys_dao.SysConfig.Table()+":"+s.sysConfigName) 107 } 108 109 var result *sys_model.TencentSdkConfToken = nil 110 newItems := garray.New() 111 for _, item := range s.TencentSdkConfTokenList { 112 if item.Identifier == identifier { 113 result = &sys_model.TencentSdkConfToken{ 114 TencentSdkConf: *info, 115 TencentAccessToken: newTokenInfo, 116 } 117 newItems.Append(*result) 118 continue 119 } 120 121 newItems.Append(*result) 122 } 123 124 if result == nil { 125 result = &sys_model.TencentSdkConfToken{ 126 TencentSdkConf: *info, 127 TencentAccessToken: newTokenInfo, 128 } 129 newItems.Append(*result) 130 } 131 132 // 返回我们需要的token信息 133 return result, nil 134 } 135 136 // GetTencentSdkConfList 获取腾讯云SDK应用配置列表 137 func (s *sSdkTencent) GetTencentSdkConfList(ctx context.Context) ([]*sys_model.TencentSdkConf, error) { 138 items := make([]*sys_model.TencentSdkConf, 0) 139 config, err := sys_service.SysConfig().GetByName(ctx, s.sysConfigName) 140 if err != nil { 141 return items, sys_service.SysLogs().ErrorSimple(ctx, gerror.New("腾讯云SDK配置信息获取失败"), "", sys_dao.SysConfig.Table()+":"+s.sysConfigName) 142 } 143 144 if config.Value == "" { 145 return items, nil 146 } 147 148 _ = gjson.DecodeTo(config.Value, &items) 149 150 return items, nil 151 } 152 153 // GetTencentSdkConf 根据identifier标识获取SDK配置信息 154 func (s *sSdkTencent) GetTencentSdkConf(ctx context.Context, identifier string) (tokenInfo *sys_model.TencentSdkConf, err error) { 155 items, err := s.GetTencentSdkConfList(ctx) 156 if err != nil { 157 return nil, err 158 } 159 160 // 循环所有配置,筛选出符合条件的配置 161 for _, conf := range items { 162 if conf.Identifier == identifier { 163 return conf, nil 164 } 165 } 166 167 return nil, sys_service.SysLogs().ErrorSimple(ctx, err, "根据 identifier 查询腾讯云SDK应用配置信息失败", sys_dao.SysConfig.Table()+":"+s.sysConfigName) 168 } 169 170 // SaveTencentSdkConf 保存SDK应用配信息, isCreate判断是更新还是新建 171 func (s *sSdkTencent) SaveTencentSdkConf(ctx context.Context, info *sys_model.TencentSdkConf, isCreate bool) (*sys_model.TencentSdkConf, error) { 172 oldItems, _ := s.GetTencentSdkConfList(ctx) 173 174 isHas := false 175 newItems := make([]*sys_model.TencentSdkConf, 0) 176 for _, conf := range oldItems { 177 if conf.Identifier == info.Identifier { // 如果标识符相等,说明已经存在, 将最新的追加到新的容器中 178 isHas = true 179 newItems = append(newItems, info) 180 continue 181 } 182 183 newItems = append(newItems, conf) // 将旧的Item追加到新的容器中 184 } 185 186 if !isHas { // 不存在 187 if isCreate { // 创建 --- 追加info (原有的 + 最新的Info) 188 newItems = append(newItems, info) 189 } else { // 更新 --- 不存在此配置,那么就提示错误 190 return nil, sys_service.SysLogs().ErrorSimple(ctx, gerror.New("腾讯云SDK配置信息保存失败,标识符错误"), "", sys_dao.SysConfig.Table()+":"+s.sysConfigName) 191 } 192 } 193 194 // 序列化后进行保存至数据库 195 jsonString := gjson.MustEncodeString(newItems) 196 _, err := sys_service.SysConfig().SaveConfig(ctx, &sys_model.SysConfig{ 197 Name: s.sysConfigName, 198 Value: jsonString, 199 }) 200 if err != nil { 201 return nil, sys_service.SysLogs().ErrorSimple(ctx, err, "腾讯云SDK配置信息保存失败", sys_dao.SysConfig.Table()+":"+s.sysConfigName) 202 } 203 204 // 同步腾讯云SDK应用配置缓存列表 205 s.syncTencentSdkConfList(ctx) 206 207 return info, nil 208 } 209 210 // syncTencentSdkConfList 同步腾讯云SDK应用配置信息列表缓存 (代码中要是用到了s.TencentSdkConfList缓存变量的话,一定需要在CUD操作后调用此方法更新缓存变量) 211 func (s *sSdkTencent) syncTencentSdkConfList(ctx context.Context) error { 212 items, err := s.GetTencentSdkConfList(ctx) 213 if err != nil { 214 return err 215 } 216 217 newTokenItems := make([]*sys_model.TencentSdkConfToken, 0) 218 for _, conf := range items { 219 for _, tokenInfo := range s.TencentSdkConfTokenList { // tokenList 220 if tokenInfo.Identifier == conf.Identifier { 221 newTokenItems = append(newTokenItems, tokenInfo) 222 } 223 } 224 } 225 226 s.TencentSdkConfTokenList = newTokenItems 227 228 return nil 229 } 230 231 // DeleteTencentSdkConf 删除腾讯云SDK应用配置信息 232 func (s *sSdkTencent) DeleteTencentSdkConf(ctx context.Context, identifier string) (bool, error) { 233 items, err := s.GetTencentSdkConfList(ctx) 234 235 isHas := false 236 newItems := garray.New(false) 237 for _, conf := range items { 238 if conf.Identifier == identifier { 239 isHas = true 240 continue 241 } 242 newItems.Append(conf) 243 } 244 245 if !isHas { 246 return false, sys_service.SysLogs().ErrorSimple(ctx, err, "要删除的腾讯云SDK配置信息不存在", sys_dao.SysConfig.Table()+":"+s.sysConfigName) 247 } 248 249 jsonString := gjson.MustEncodeString(newItems) 250 _, err = sys_service.SysConfig().SaveConfig(ctx, &sys_model.SysConfig{ 251 Name: s.sysConfigName, 252 Value: jsonString, 253 }) 254 if err != nil { 255 return false, sys_service.SysLogs().ErrorSimple(ctx, err, "腾讯云SDK配置信息删除失败", sys_dao.SysConfig.Table()+":"+s.sysConfigName) 256 } 257 258 // 同步Token列表 259 s.syncTencentSdkConfList(ctx) 260 261 return true, nil 262 } 263 264 // 腾讯云服务的具体应用实例 265 266 // LivenessRecognition 人脸核身(SDK接入模式) 267 func (s *sSdkTencent) LivenessRecognition(ctx context.Context, idCard, name, livenessType string) { 268 // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密 269 // 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305 270 // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取 271 272 config, err := s.GetTencentSdkConf(ctx, "tencent_config") 273 274 credential := common.NewCredential( 275 config.AESKey, 276 config.SecretKey, 277 ) 278 // 实例化一个client选项,可选的,没有特殊需求可以跳过 279 cpf := profile.NewClientProfile() 280 cpf.HttpProfile.Endpoint = "faceid.tencentcloudapi.com" 281 // 实例化要请求产品的client对象,clientProfile是可选的 282 client, _ := faceid.NewClient(credential, "", cpf) 283 284 // 实例化一个请求对象,每个接口都会对应一个request对象 285 request := faceid.NewLivenessRecognitionRequest() 286 request.IdCard = common.StringPtr(idCard) 287 request.Name = common.StringPtr(name) 288 request.LivenessType = common.StringPtr(livenessType) 289 290 // 返回的resp是一个LivenessRecognitionResponse的实例,与请求对象对应 291 response, err := client.LivenessRecognition(request) 292 if _, ok := err.(*errors.TencentCloudSDKError); ok { 293 fmt.Printf("An API error has returned: %s", err) 294 return 295 } 296 if err != nil { 297 panic(err) 298 } 299 // 输出json格式的字符串回包 300 fmt.Printf("%s", response.ToJsonString()) 301 302 } 303 304 // DetectAuth 腾讯云-实名核身鉴权 305 func (s *sSdkTencent) DetectAuth(ctx context.Context, idCard, name, returnUrl string) (*sys_model.DetectAuthRes, error) { 306 // 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密 307 // 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305 308 // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取 309 config, err := s.GetTencentSdkConf(ctx, "tencent_config") 310 311 credential := common.NewCredential( 312 config.AESKey, 313 config.SecretKey, 314 ) 315 // 实例化一个client选项,可选的,没有特殊需求可以跳过 316 cpf := profile.NewClientProfile() 317 cpf.HttpProfile.Endpoint = "faceid.tencentcloudapi.com" 318 // 实例化要请求产品的client对象,clientProfile是可选的 319 client, _ := faceid.NewClient(credential, "", cpf) 320 321 // 实例化一个请求对象,每个接口都会对应一个request对象 322 request := faceid.NewDetectAuthRequest() 323 324 request.RuleId = common.StringPtr("1") // TODO 现在先硬编码写死了,后续RuleId应该做数据库管理 325 request.IdCard = common.StringPtr(idCard) 326 request.Name = common.StringPtr(name) 327 request.RedirectUrl = common.StringPtr(returnUrl) 328 329 // 返回的resp是一个DetectAuthResponse的实例,与请求对象对应 330 response, err := client.DetectAuth(request) 331 if _, ok := err.(*errors.TencentCloudSDKError); ok { 332 fmt.Printf("An API error has returned: %s", err) 333 return nil, sys_service.SysLogs().ErrorSimple(ctx, err, "An API error has returned: "+err.Error(), s.sysConfigName) 334 } 335 if err != nil { 336 return nil, sys_service.SysLogs().ErrorSimple(ctx, err, "腾讯云-实名核身鉴权请求失败", s.sysConfigName) 337 } 338 // 输出json格式的字符串回包 339 fmt.Printf("%s", response.ToJsonString()) 340 341 res := sys_model.DetectAuthRes{} 342 if response.Response != nil { 343 _ = gjson.DecodeTo(response.Response, &res) 344 345 } 346 347 return &res, err 348 349 /* 350 响应示例: 351 {"Response":{"Url":"https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx1f7125112b74db52\u0026redirect_uri=https%3A%2F%2Fopen.faceid.qq.com%2Fv1%2Fapi%2FgetCode%3FbizRedirect%3Dhttps%253A%252F%252Ffaceid.qq.com%252Fapi%252Fauth%252FgetOpenidAndSaveToken%253Ftoken%253D43DCB8C3-D330-429C-AF46-DB73BA9EE794\u0026response_type=code\u0026scope=snsapi_base\u0026state=\u0026component_appid=wx9802ee81e68d6dee#wechat_redirect","BizToken":"43DCB8C3-D330-429C-AF46-DB73BA9EE794","RequestId":"95be8b7f-f6f8-4735-895f-ee1c3d1f4ab9"}} 352 */ 353 } 354 355 // GetDetectAuthResult 获取腾讯云-实名核身鉴权结果 356 func (s *sSdkTencent) GetDetectAuthResult(ctx context.Context, bizToken string, ruleId ...string) (*sys_model.GetDetectAuthResultRes, error) { 357 config, err := s.GetTencentSdkConf(ctx, "tencent_config") 358 credential := common.NewCredential( 359 config.AESKey, 360 config.SecretKey, 361 ) 362 // 实例化一个client选项,可选的,没有特殊需求可以跳过 363 cpf := profile.NewClientProfile() 364 cpf.HttpProfile.Endpoint = "faceid.tencentcloudapi.com" 365 // 实例化要请求产品的client对象,clientProfile是可选的 366 client, _ := faceid.NewClient(credential, "", cpf) 367 368 // 实例化一个请求对象,每个接口都会对应一个request对象 369 request := faceid.NewGetDetectInfoRequest() 370 371 request.BizToken = common.StringPtr(bizToken) 372 id := "1" 373 request.RuleId = common.StringPtr(id) // TODO 现在先硬编码写死了,后续RuleId应该做数据库管理 374 //request.RuleId = common.StringPtr(ruleId) 375 376 // 返回的resp是一个GetDetectInfoResponse的实例,与请求对象对应 377 response, err := client.GetDetectInfo(request) 378 if _, ok := err.(*errors.TencentCloudSDKError); ok { 379 fmt.Printf("An API error has returned: %s", err) 380 return nil, sys_service.SysLogs().ErrorSimple(ctx, err, "An API error has returned: "+err.Error(), s.sysConfigName) 381 } 382 if err != nil { 383 panic(err) 384 } 385 // 输出json格式的字符串回包 386 //fmt.Printf("%s", response.ToJsonString()) 387 388 var tempInfo struct { 389 Response struct { 390 RequestId *string `json:"RequestId,omitnil,omitempty" name:"RequestId" ` 391 392 DetectInfo *string `json:"DetectInfo,omitnil,omitempty" name:"DetectInfo" ` 393 } `json:"Response"` 394 } 395 396 res := sys_model.GetDetectAuthResultRes{} 397 if response.Response != nil { 398 //gjson.DecodeTo(response.Response.DetectInfo, &res) 399 400 //jsonString := response.ToJsonString() 401 //gjson.DecodeTo(jsonString, &res) 402 403 t := kconv.Struct(response.ToJsonString(), &tempInfo) 404 jsonStr, _ := json.UnescapeJSON(*t.Response.DetectInfo) 405 gjson.DecodeTo(jsonStr, &res) 406 res.RequestId = response.Response.RequestId 407 } 408 409 return &res, err 410 411 /* 412 响应示例: 413 { 414 "Text": { 415 "ErrCode": 0, 416 "ErrMsg": "成功", 417 "IdCard": "****", 418 "Name": "林菲菲", 419 "OcrNation": null, 420 "OcrAddress": null, 421 "OcrBirth": null, 422 "OcrAuthority": null, 423 "OcrValidDate": null, 424 "OcrName": "", 425 "OcrIdCard": "", 426 "OcrGender": null, 427 "LiveStatus": 0, 428 "LiveMsg": "成功", 429 "Comparestatus": 0, 430 "Comparemsg": "成功", 431 "Sim": "96.42", 432 "Location": null, 433 "Extra": "", 434 "Detail": { 435 "LivenessData": [ 436 { 437 "ErrCode": 0, 438 "ErrMsg": "成功", 439 "ReqTime": "1715329563289", 440 "IdCard": "*****", 441 "Name": "林菲菲" 442 } 443 ] 444 } 445 }, 446 "IdCardData": { 447 "OcrFront": null, 448 "OcrBack": null 449 }, 450 "BestFrame": { 451 "BestFrame": "" 452 } 453 } 454 */ 455 } 456 457 // GetDetectAuthPlusResponse 获取腾讯云-实名核身鉴权增强版结果 (v3.0接口) 458 func (s *sSdkTencent) GetDetectAuthPlusResponse(ctx context.Context, bizToken, ruleId string) (*sys_model.GetDetectAuthPlusResponseRes, error) { 459 config, err := s.GetTencentSdkConf(ctx, "tencent_config") 460 461 credential := common.NewCredential( 462 config.AESKey, 463 config.SecretKey, 464 ) 465 // 实例化一个client选项,可选的,没有特殊需求可以跳过 466 cpf := profile.NewClientProfile() 467 cpf.HttpProfile.Endpoint = "faceid.tencentcloudapi.com" 468 // 实例化要请求产品的client对象,clientProfile是可选的 469 client, _ := faceid.NewClient(credential, "", cpf) 470 471 // 实例化一个请求对象,每个接口都会对应一个request对象 472 request := faceid.NewGetDetectInfoEnhancedRequest() 473 474 request.BizToken = common.StringPtr(bizToken) 475 request.RuleId = common.StringPtr(ruleId) 476 477 // 返回的resp是一个GetDetectInfoEnhancedResponse的实例,与请求对象对应 478 response, err := client.GetDetectInfoEnhanced(request) 479 if _, ok := err.(*errors.TencentCloudSDKError); ok { 480 fmt.Printf("An API error has returned: %s", err) 481 return nil, sys_service.SysLogs().ErrorSimple(ctx, err, "An API error has returned: "+err.Error(), s.sysConfigName) 482 } 483 if err != nil { 484 return nil, sys_service.SysLogs().ErrorSimple(ctx, err, "腾讯云-获取实名核身鉴权增强版结果请求失败", s.sysConfigName) 485 } 486 487 // 输出json格式的字符串回包 488 fmt.Printf("%s", response.ToJsonString()) 489 490 res := sys_model.GetDetectAuthPlusResponseRes{} 491 if response.Response != nil { 492 _ = gjson.DecodeTo(response.Response, &res) 493 } 494 495 return &res, err 496 497 /* 498 响应示例: 499 500 */ 501 } 502 503 // LivenessRecognition_Http 人脸核身(HTTP接入模式) 504 func (s *sSdkTencent) LivenessRecognition_Http(ctx context.Context, action, secretId, secretKey string) { // LivenessRecognition 活体核验 505 service := "faceid" 506 version := "2018-03-01" 507 //action := "LivenessRecognition" 508 region := "" 509 token := "" 510 host := "faceid.tencentcloudapi.com" 511 algorithm := "TC3-HMAC-SHA256" 512 var timestamp = time.Now().Unix() 513 514 // ************* 步骤 1:拼接规范请求串 ************* 515 httpRequestMethod := "POST" 516 canonicalURI := "/" 517 canonicalQueryString := "" 518 contentType := "application/json; charset=utf-8" 519 canonicalHeaders := fmt.Sprintf("content-type:%s\nhost:%s\nx-tc-action:%s\n", 520 contentType, host, strings.ToLower(action)) 521 signedHeaders := "content-type;host;x-tc-action" 522 payload := "{}" 523 hashedRequestPayload := sha256hex(payload) 524 canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", 525 httpRequestMethod, 526 canonicalURI, 527 canonicalQueryString, 528 canonicalHeaders, 529 signedHeaders, 530 hashedRequestPayload) 531 log.Println(canonicalRequest) 532 533 // ************* 步骤 2:拼接待签名字符串 ************* 534 date := time.Unix(timestamp, 0).UTC().Format("2006-01-02") 535 credentialScope := fmt.Sprintf("%s/%s/tc3_request", date, service) 536 hashedCanonicalRequest := sha256hex(canonicalRequest) 537 string2sign := fmt.Sprintf("%s\n%d\n%s\n%s", 538 algorithm, 539 timestamp, 540 credentialScope, 541 hashedCanonicalRequest) 542 log.Println(string2sign) 543 544 // ************* 步骤 3:计算签名 ************* 545 secretDate := hmacsha256(date, "TC3"+secretKey) 546 secretService := hmacsha256(service, secretDate) 547 secretSigning := hmacsha256("tc3_request", secretService) 548 signature := hex.EncodeToString([]byte(hmacsha256(string2sign, secretSigning))) 549 log.Println(signature) 550 551 // ************* 步骤 4:拼接 Authorization ************* 552 authorization := fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s", 553 algorithm, 554 secretId, 555 credentialScope, 556 signedHeaders, 557 signature) 558 log.Println(authorization) 559 560 // ************* 步骤 5:构造并发起请求 ************* 561 url := "https://" + host 562 httpRequest, _ := http.NewRequest("POST", url, strings.NewReader(payload)) 563 httpRequest.Header = map[string][]string{ 564 "Host": {host}, 565 "X-TC-Action": {action}, 566 "X-TC-Version": {version}, 567 "X-TC-Timestamp": {strconv.FormatInt(timestamp, 10)}, 568 "Content-Type": {contentType}, 569 "Authorization": {authorization}, 570 } 571 if region != "" { 572 httpRequest.Header["X-TC-Region"] = []string{region} 573 } 574 if token != "" { 575 httpRequest.Header["X-TC-Token"] = []string{token} 576 } 577 httpClient := http.Client{} 578 resp, err := httpClient.Do(httpRequest) 579 if err != nil { 580 log.Println(err) 581 return 582 } 583 defer resp.Body.Close() 584 body := &bytes.Buffer{} 585 _, err = body.ReadFrom(resp.Body) 586 if err != nil { 587 log.Println(err) 588 return 589 } 590 log.Println(body.String()) 591 } 592 593 func sha256hex(s string) string { 594 b := sha256.Sum256([]byte(s)) 595 return hex.EncodeToString(b[:]) 596 } 597 598 func hmacsha256(s, key string) string { 599 hashed := hmac.New(sha256.New, []byte(key)) 600 hashed.Write([]byte(s)) 601 return string(hashed.Sum(nil)) 602 }