美文网首页
NestJS配置阿里云短信,使用V1或V3签名验证

NestJS配置阿里云短信,使用V1或V3签名验证

作者: Poppy11 | 来源:发表于2021-09-18 17:25 被阅读0次

V1验证算法

这里有一个坑就是,生成的签名,将PhoneNumberSet和SignName都URL解码了。

import { HttpException, Injectable } from '@nestjs/common'
import { commonJiraJSONConfig } from './commonFun';
import { proxyAxios } from './proxyAxios';
var urlencode = require('urlencode')
const crypto = require('crypto')


@Injectable()
export class commonSMSService {
  private sort_params(params) {
    let strParam = "";
    let keys = Object.keys(params);
    keys.sort();
    for (let k in keys) {
      //k = k.replace(/_/g, '.');
      strParam += ("&" + keys[k] + "=" + params[keys[k]]);
    }
    return strParam
  }

  private formatSignString(reqMethod, endpoint, path, strParam) {
    let strSign = reqMethod + endpoint + path + "?" + strParam.slice(1);
    return strSign;
  }

  private sha1(secretKey, strsign) {
    let signMethodMap = { 'HmacSHA1': "sha1" };
    let hmac = crypto.createHmac(signMethodMap['HmacSHA1'], secretKey || "");
    return hmac.update(Buffer.from(strsign, 'utf8')).digest('base64')
  }

  private get_req_url(params, endpoint) {
    params['Signature'] = escape(params['Signature']);
    params['PhoneNumberSet.0'] = urlencode(params['PhoneNumberSet.0']);
    params['SignName'] = urlencode(params['SignName']);
    const url_strParam = this.sort_params(params)
    return "https://" + endpoint + "/?" + url_strParam.slice(1);
  }

  protected async tencentAutograph(phone: string, code: string, type: string) {
    const jiraMessagesJSON = await commonJiraJSONConfig('Messages.json')
    const jiraMessages = jiraMessagesJSON[type]
    const SECRET_ID = "SECRET_ID "
    const SECRET_KEY = "SECRET_KEY "
    const endpoint = "sms.tencentcloudapi.com"
    const Region = 'ap-guangzhou'
    const Version = '2021-01-11'
    const Action = 'SendSms'
    const Timestamp = Math.round(new Date().getTime() / 1000)
    const Nonce = code
    const obj = {
      'Action': Action,
      'Language': 'zh-CN',
      'Nonce': Nonce,
      'PhoneNumberSet.0': `+86${phone}`,
      'Region': Region,
      'SecretId': SECRET_ID,
      'SignName': jiraMessages?.signature?.content,
      'SmsSdkAppId': '1400557301',
      'TemplateId': jiraMessages?.body?.id,
      'TemplateParamSet.0': code,
      'Timestamp': Timestamp,
      'Version': Version
    }
    // 1. 对参数排序,并拼接请求字符串
    const strParam = this.sort_params(obj)

    // 2. 拼接签名原文字符串
    const reqMethod = "GET";
    const path = "/";
    const strSign = this.formatSignString(reqMethod, endpoint, path, strParam)

    // 3. 生成签名串
    obj['Signature'] = this.sha1(SECRET_KEY, strSign)

    const req_url = this.get_req_url(obj, endpoint)
    return req_url
  }

  public async axiosSMSUrl(phone: string, code: string, type: string) {
    try {
      const getUrl = await this.tencentAutograph(phone, code, type)
      const sendSMSRes: any = await proxyAxios(getUrl,false,'GET')
      if (sendSMSRes.Response?.SendStatusSet[0]?.Code != 'Ok') {
        throw new HttpException("验证码发送失败", 500)
      }
    } catch {
      throw new HttpException("验证码发送失败", 500)
    }
  }
}



V3验证算法

import { Injectable } from '@nestjs/common'
import { commonJiraJSONConfig } from './commonFun';
import { proxyAxios } from './proxyAxios';
const crypto = require('crypto')



@Injectable()
export class commonSMSService {

  sha256(message, secret = '', encoding?) {
    const hmac = crypto.createHmac('sha256', secret)
    return hmac.update(message).digest(encoding)
  }

  getHash(message, encoding = 'hex') {
    const hash = crypto.createHash('sha256')
    return hash.update(message).digest(encoding)
  }

  getDate(timestamp) {
    const date = new Date(timestamp * 1000)
    const year = date.getUTCFullYear()
    const month = ('0' + (date.getUTCMonth() + 1)).slice(-2)
    const day = ('0' + date.getUTCDate()).slice(-2)
    return `${year}-${month}-${day}`
  }

  async getBody(phone: string, code: string, type: string) {
    let jiraMessagesJSON
    if (type !== 'verify') {
      jiraMessagesJSON = await commonJiraJSONConfig('Messages.json')
    } else {
      jiraMessagesJSON = await commonJiraJSONConfig('Messages.json', 573686)
    }
    const jiraMessages = jiraMessagesJSON[type]
    return {
      "PhoneNumberSet": [
        `+86${phone}`
      ],
      "SmsSdkAppId": "1400557301",
      'SignName': jiraMessages?.signature?.content,
      'TemplateId': jiraMessages?.body?.id,
      "TemplateParamSet": [
        `${code}`
      ]
    }
  }

  async axiosSMSUrl(phone: string, code: string, type: string) {
    // 密钥参数
    const SECRET_ID = "AKIDObXDckbCWYJo1xby5uSijTw84j7D2bss"
    const SECRET_KEY = "kDPKxKRHq89tLWR6UbT5RVIo31i8H9k5"

    const endpoint = "sms.tencentcloudapi.com"
    const service = "sms"
    const region = "ap-guangzhou"
    const action = "SendSms"
    const version = "2021-01-11"
    const timestamp = Math.round(Date.now() / 1000)
    console.log(timestamp)
    //时间处理, 获取世界时间日期
    const date = this.getDate(timestamp)
    // ************* 步骤 1:拼接规范请求串 *************
    const signedHeaders = "content-type;host"

    const payload = await this.getBody(phone, code, type)
    const hashedRequestPayload = this.getHash(JSON.stringify(payload));
    const httpRequestMethod = "POST"
    const canonicalUri = "/"
    const canonicalQueryString = ""
    const canonicalHeaders = "content-type:application/json\n" + "host:" + endpoint + "\n"

    const canonicalRequest = httpRequestMethod + "\n"
      + canonicalUri + "\n"
      + canonicalQueryString + "\n"
      + canonicalHeaders + "\n"
      + signedHeaders + "\n"
      + hashedRequestPayload
    console.log(canonicalRequest)

    // ************* 步骤 2:拼接待签名字符串 *************
    const algorithm = "TC3-HMAC-SHA256"
    const hashedCanonicalRequest = this.getHash(canonicalRequest);
    const credentialScope = date + "/" + service + "/" + "tc3_request"
    const stringToSign = algorithm + "\n" +
      timestamp + "\n" +
      credentialScope + "\n" +
      hashedCanonicalRequest
    console.log(stringToSign)

    // ************* 步骤 3:计算签名 *************
    const kDate = this.sha256(date, 'TC3' + SECRET_KEY)
    const kService = this.sha256(service, kDate)
    const kSigning = this.sha256('tc3_request', kService)
    const signature = this.sha256(stringToSign, kSigning, 'hex')
    console.log(signature)

    // ************* 步骤 4:拼接 Authorization *************
    const authorization = algorithm + " " +
      "Credential=" + SECRET_ID + "/" + credentialScope + ", " +
      "SignedHeaders=" + signedHeaders + ", " +
      "Signature=" + signature
    console.log(authorization)

    try {
      const sendSMSRes: any = await proxyAxios(`https://${endpoint}`, false, 'POST', payload,
        {
          "Authorization": authorization,
          "Content-Type": "application/json",
          "Host": endpoint,
          "X-TC-Action": action,
          "X-TC-Timestamp": timestamp.toString(),
          "X-TC-Version": version,
          "X-TC-Region": region
        }
      )
      if (sendSMSRes.Response?.SendStatusSet?.[0]?.Code == 'Ok') {
        return 'ok'
      } else {
        return "sendFail"
      }
    } catch {
      return "sendFail"
    }

  }

}

相关文章

网友评论

      本文标题:NestJS配置阿里云短信,使用V1或V3签名验证

      本文链接:https://www.haomeiwen.com/subject/ffycbltx.html