# 实现签名认证

使用签名认证时,参数如下表所示:

参数名称

类型

必选

描述

X-Jcc-Timestamp

Integer

当前UNIX时间戳,可记录发起API请求的时间。

例如 1529223702。

注意:如果与服务器时间相差超过20秒,会引起签名过期错误。

X-Jcc-Service

String

服务名称,当前固定取值:jcc-api

X-Jcc-Authorization

String

HTTP 身份认证头部字段,凭证信息生成方式在实现方法中说明。

# 实现方法

按如下格式拼接待签名字符串:

StringToSign = Date + '/' + `jcc-api` ​

字段名称 描述
Date 请求当前日期,日期需要满足格式yyyy-mm-dd。
  • 计算签名

    计算签名的伪代码如下:

Signature = Base64(HMAC_SHA256(APISecret, StringToSign))

  • 拼接 Authorization

按如下格式拼接 Authorization:

Authorization =
    Algorithm + ' ' +
    'key-id=' + SecretId + ',' +
    'signed-headers=' + SignedHeaders + ', ' +
    'Signature=' + Signature

字段名称

描述

Algorithm

签名方法,固定为:J-HMAC-SHA256

SecretId

密钥对中API Key。

SignedHeaders

参与签名的头部信息。此示例取值为 x-jcc-timestamp;x-jcc-service。

Signature

签名值。

最终完整的请求头如下:

X-Jcc-Timestamp: 1641370291
X-Jcc-Service: jcc-api
X-Jcc-Authorization: J-HMAC-SHA256 key-id="key-123",signed-headers="x-jcc-timestamp;x-jcc-service",signature="TrGC3BXmqVKEW89d35favUtYl6hIpcce%2BBQiodUqyRI%3D"

# 示例代码

  • JAVA 示例代码采用Spring Boot框架自带的RestTemplate,如果采用其他HTTP框架请自行支持POST重定向。
    1. 支持POST的重定向
package com.juphoon.rcs.controller;

import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;

@Configuration
public class RestTemplateConfig {

   @Bean("laxRestTemplate")
   public RestTemplate laxRestTemplate() {
      HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
      httpRequestFactory.setConnectionRequestTimeout(2000);
      httpRequestFactory.setConnectTimeout(10000);
      httpRequestFactory.setReadTimeout(72000);
      HttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();
      httpRequestFactory.setHttpClient(httpClient);
      RestTemplate restTemplate = new RestTemplate(httpRequestFactory);
      return restTemplate;
   }

}
  1. 关键代码逻辑
  package com.juphoon.rcs.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import sun.misc.BASE64Encoder;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;

@Slf4j
@RequestMapping("/test")
@RestController
public class TestController {
    private static EchoEncoder encoder = new EchoEncoder();
    @Autowired
    private RestTemplate laxRestTemplate;
    @GetMapping("/hmac")
    public void hmacAuth() {
        // 客户 API Key
        final String customerAPIKey = "Your customer api key";
        // 客户 API Secret
        final String customerAPISecret = "Your customer api secret";
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        String signedHeaders = "x-jcc-timestamp;x-jcc-service";
        long time = System.currentTimeMillis();
        String service = "jcc-api";
        String date = date(time);
        String signatureString = date + "/" + service;
        String sign = null;
        try {
            sign = HMACSHA256(signatureString, customerAPISecret);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String auth = "J-HMAC-SHA256 key-id=\"" + customerAPIKey + "\"," +
            "signed-headers=\"" + signedHeaders + "\",signature=\"" + sign + "\"";
        httpHeaders.add("X-Jcc-Timestamp", time / 1000 + "");
        httpHeaders.add("X-Jcc-Service", "jcc-api");
        httpHeaders.add("X-Jcc-Authorization", auth);
        String body = "Your Send Payload Json String";
        String urlString = "https://sms.api.juphoon.com/sms/v1";
        HttpEntity requestEntity = new HttpEntity<>(body, httpHeaders);
        ResponseEntity<String> response = laxRestTemplate.exchange(urlString, HttpMethod.POST, requestEntity, String.class);
    }
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        String encode = encoder.encode(array);
        return URLEncoder.encode(encode, "UTF-8");
    }
    public static String date(Long time) {
        Date date = new Date(time);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(date);
    }
}
  • Golang
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
    "strconv"
    "strings"
    "time"
)

const (
    // 客户 API Key
    customerAPIKey = "Your customer key"
    // 客户 API Secret
    customerAPISecret = "Your customer secret"
)

// HMac 认证实现
func main() {
    client := &http.Client{}
    req, err := http.NewRequest(
        "POST",
        "https://sms.api.juphoon.com/sms/v1",
        strings.NewReader("Your send payload string"))
    if err != nil {
        fmt.Println(err)
        return
    }

    // 增加 Authorization header
    req.Header.Add("Content-Type", "application/json")
    jHmacSha256(req)

    // 发送 HTTP 请求
    res, err := client.Do(req)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer res.Body.Close()

    body, err := ioutil.ReadAll(res.Body)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println(string(body))
}

func jHmacSha256(req *http.Request) {
    // 服务和签名头
    service := "jcc-api"
    signedHeaders := "x-jcc-timestamp;x-jcc-service"

    // 生成时间戳
    timestamp := time.Now().Unix()
    date := time.Unix(timestamp, 0).UTC().Format("2006-01-02")

    // 组装签名头
    signatureString := fmt.Sprintf("%s/%s", date, service)

    // Sha1签名头
    key := []byte(customerAPISecret)
    h := hmac.New(sha256.New, key)
    h.Write([]byte(signatureString))

    // Base64和URL Encode
    sigString := base64.StdEncoding.EncodeToString(h.Sum(nil))
    encodedString := url.QueryEscape(sigString)

    // 组装授权信息
    authToken := fmt.Sprintf(`J-HMAC-SHA256 key-id=%q,signed-headers=%q,signature=%q`,
        customerAPIKey, signedHeaders, encodedString)

    tt := strconv.Itoa(int(timestamp))

    req.Header.Set("x-jcc-timestamp", tt)
    req.Header.Set("x-jcc-service", service)
    req.Header.Set("x-jcc-authorization", authToken)
}
最后更新时间: 4/22/2022, 9:23:40 AM