Skip to content

开发者接入指南-登录鉴权


云从公有云平台使用OAuth2.0授权调用开放API,开发者调用开放能力前,需要先使用申请的App Key、App Secret获取令牌access_token,然后携带access_token调用开放能力。

能力调用域名:https://api-ai.cloudwalk.com

  • 注意: 请额外依赖下述JAR包:

bcprov-jdk15to18

4.1. Token获取

接口说明:

用于获取访问云从公有云平台开放能力的身份令牌,为保障系统安全性,开发者需要安全管理令牌。令牌有过期时间,过期前可以重复使用,令牌的过期维护需要调用方自己处理。

调用流程:

An image

请求 URL:

/sso/oauth/token

请求参数

  • method: POST

  • 请求头参数:"Content-Type":"application/x-www-form-urlencoded"

  • 表单参数:

参数名称必填数据类型长度参数描述
clientIdString1-32客户端ID,对应创建应用生成的App Key
clientSecretString1-255加密后的客户端密钥,使用创建应用生成的App Secret和SM2公钥进行加密
scopeString1-32权限范围,固定值为scope

请求示例:

        Content-Type: application/x-www-form-urlencoded
              scope:scope
              clientId:xxxxxxxx
              clientSecret:xxxxxxxxxx

响应参数

参数名称数据类型长度参数描述
access_tokenString1-32访问令牌
token_typeString1-32令牌类型 bearer
expires_inLong1-32令牌有效期,单位:秒,默认12小时
scopeString1-32授权范围 返回scope

响应示例:

{
    "access_token": "xxxxxxxxxxxxxx",
    "token_type": "bearer",
    "expires_in": 170932,
    "scope": "scope"
}

示例代码(JAVA)

java
package cloudwalk.kaleidoscope;

import cn.hutool.crypto.ECKeyUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.bouncycastle.crypto.engines.SM2Engine;

import java.util.HashMap;
import java.util.Map;

public class tokenTest {

    /**
     * sm2-sm4加密算法的应用详情中的App Key和App Secret、SM2公钥
     */
    public static String APP_KEY = "xxxxxxxx";
    public static String APP_SECRET = "xxxxxxxxxxxxxxxx";
    public static String SM2_PUB = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";
    /**
     * 调用地址
     */
    public static String HTTP_ADDRESS = "https://api-ai.cloudwalk.com";
    /**
     * Token管理请求路径
     */
    private static final String TOKEN_URL = HTTP_ADDRESS + "/sso/oauth/token";

    public static void main(String[] args) throws Exception {
        getToken();
    }

    private static String sm2Encrypt(String plain) {
        SM2 sm2 = new SM2(null, ECKeyUtil.toSm2PublicParams("04".concat(SM2_PUB)));
        sm2.setMode(SM2Engine.Mode.C1C2C3);
        return sm2.encryptHex(plain, KeyType.PublicKey);
    }


    private static String getToken() {
        Map<String, String> paramMap = new HashMap<>(2);

        paramMap.put("clientId", APP_KEY);
        paramMap.put("clientSecret", sm2Encrypt(APP_SECRET));

        HttpRequest request = HttpUtil.createPost(HTTP_TEST_URL + uri);
        request.form(paramMap); 
        String result = request.execute().body();

        System.out.println(result);

        JSONObject object = JSON.parseObject(result);
        return object.getString("access_token");
    }

}

示例代码(GO语言)

go
package main

import (
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"math/big"
	"net/http"
	"net/url"
	"strings"

	"github.com/tjfoc/gmsm/sm2"
)

const (
	// sm2-sm4加密算法的应用详情中的App Key和App Secret、SM2公钥
	APP_KEY    = "xxxxxxxx"
	APP_SECRET = "xxxxxxxxxxxxxxxx"
	SM2_PUB    = "xxxxxxxxxxxxxxxxxxxxxxxxxxx"

	// 调用地址
	HTTP_ADDRESS = "https://api-ai.cloudwalk.com"

	// Token管理请求路径
	TOKEN_URL = HTTP_ADDRESS + "/sso/oauth/token"
)

func main() {
	getToken()
}

// sm2Encrypt 使用SM2公钥加密明文,返回十六进制字符串
// 对应Java: sm2.encryptHex(plain, KeyType.PublicKey) with Mode.C1C2C3
func sm2Encrypt(plain string) (string, error) {
	// Java代码中使用 "04" + SM2_PUB 作为未压缩公钥
	pubKeyHex := "04" + SM2_PUB
	pubBytes, err := hex.DecodeString(pubKeyHex)
	if err != nil {
		return "", fmt.Errorf("decode public key hex failed: %v", err)
	}

	// 未压缩公钥格式: 04 + X(32字节) + Y(32字节) = 65字节
	if len(pubBytes) != 65 || pubBytes[0] != 0x04 {
		return "", fmt.Errorf("invalid uncompressed public key format, expected 65 bytes with 0x04 prefix")
	}

	// 解析 X 和 Y 坐标
	curve := sm2.P256Sm2()
	x := new(big.Int).SetBytes(pubBytes[1:33])
	y := new(big.Int).SetBytes(pubBytes[33:65])

	// 创建公钥
	pubKey := &sm2.PublicKey{
		Curve: curve,
		X:     x,
		Y:     y,
	}

	// 使用 C1C2C3 模式加密(与 Java Hutool 的 SM2Engine.Mode.C1C2C3 一致)
	cipher, err := sm2.Encrypt(pubKey, []byte(plain), rand.Reader, sm2.C1C2C3)
	if err != nil {
		return "", fmt.Errorf("sm2 encrypt failed: %v", err)
	}

	return hex.EncodeToString(cipher), nil
}

// getToken 获取访问令牌
func getToken() (string, error) {
	// 使用SM2加密APP_SECRET
	encryptedSecret, err := sm2Encrypt(APP_SECRET)
	if err != nil {
		return "", fmt.Errorf("encrypt APP_SECRET failed: %v", err)
	}

	// 准备表单参数
	formData := url.Values{}
	formData.Set("clientId", APP_KEY)
	formData.Set("clientSecret", encryptedSecret)

	// 创建POST请求
	req, err := http.NewRequest(http.MethodPost, TOKEN_URL, strings.NewReader(formData.Encode()))
	if err != nil {
		return "", fmt.Errorf("create request failed: %v", err)
	}

	// 设置请求头
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	// 发送请求
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("send request failed: %v", err)
	}
	defer resp.Body.Close()

	// 读取响应
	respBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("read response failed: %v", err)
	}

	// 打印原始响应
	fmt.Println(string(respBytes))

	// 解析JSON响应
	var result map[string]interface{}
	if err := json.Unmarshal(respBytes, &result); err != nil {
		return "", fmt.Errorf("parse json failed: %v, response: %s", err, string(respBytes))
	}

	// 提取access_token
	accessToken, ok := result["access_token"].(string)
	if !ok {
		return "", fmt.Errorf("access_token not found in response, response: %s", string(respBytes))
	}

	return accessToken, nil
}

内容输出:

{"access_token":"xxxxxxxx","token_type":"bearer","expires_in":172342,"scope":"scope"}