All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.nls.client.AccessToken Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2015 Alibaba Group Holding Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.alibaba.nls.client;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.SimpleTimeZone;
import java.util.UUID;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

/**
 * 访问令牌是用户访问智能语音服务的凭证
 *
 * @author xuebin
 */
public class AccessToken {

    private static final String NLS_DOMAIN = "nls-meta.cn-shanghai.aliyuncs.com";

    private static final String NLS_VERSION = "2019-02-28";

    private static final String MCOS_DOMAIN = "mcos.cn-shanghai.aliyuncs.com";

    private static final String MCOS_VERSION = "2022-08-11";

    private final static String TIME_ZONE = "GMT";

    private final static String FORMAT_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss'Z'";

    private final static String URL_ENCODING = "UTF-8";

    private static final String ALGORITHM_NAME = "HmacSHA1";

    private static final String ENCODING = "UTF-8";

    private static Logger logger = LoggerFactory.getLogger(AccessToken.class);

    private String accessKeyId;

    private String accessKeySecret;

    private String domain = "";

    private String regionId = "cn-shanghai";

    private String version = "";

    private String action = "CreateToken";

    private String token;

    private long expireTime;

    /**
     * 构造实例
     *
     * @param accessKeyId     阿里云akid
     * @param accessKeySecret 阿里云secret key
     */
    public AccessToken(String accessKeyId, String accessKeySecret) {
        this.accessKeyId = accessKeyId;
        this.accessKeySecret = accessKeySecret;
        domain = NLS_DOMAIN;
        version = NLS_VERSION;
    }

    public AccessToken(String accessKeyId, String accessKeySecret, AccessTokenType type) {
        this.accessKeyId = accessKeyId;
        this.accessKeySecret = accessKeySecret;
        if (AccessTokenType.MCOS.equals(type)) {
            domain = MCOS_DOMAIN;
            version = MCOS_VERSION;
        } else {
            domain = NLS_DOMAIN;
            version = NLS_VERSION;
        }
    }

    /**
     * @param accessKeyId     阿里云akid
     * @param accessKeySecret 阿里云secret key
     * @param domain          服务域名
     * @param regionId        服务的地域ID
     * @param version         API的版本
     */
    public AccessToken(String accessKeyId, String accessKeySecret,
                       String domain, String regionId, String version) {
        this.accessKeyId = accessKeyId;
        this.accessKeySecret = accessKeySecret;
        this.domain = domain;
        this.regionId = regionId;
        this.version = version;
    }

    /**
     * 获取时间戳 必须符合ISO8601规范,并需要使用UTC时间,时区为+0
     */
    private String getISO8601Time(Date date) {
        Date nowDate = date;
        if (null == date) {
            nowDate = new Date();
        }
        SimpleDateFormat df = new SimpleDateFormat(FORMAT_ISO8601);
        df.setTimeZone(new SimpleTimeZone(0, TIME_ZONE));
        return df.format(nowDate);
    }

    /**
     * 获取UUID
     */
    private String getUniqueNonce() {
        UUID uuid = UUID.randomUUID();
        return uuid.toString();
    }

    /**
     * URL编码 使用UTF-8字符集按照 RFC3986 规则编码请求参数和参数取值
     */
    private String percentEncode(String value) throws UnsupportedEncodingException {
        return value != null ? URLEncoder.encode(value, URL_ENCODING).replace("+", "%20")
            .replace("*", "%2A").replace("%7E", "~") : null;
    }

    /***
     * 将参数排序后,进行规范化设置,组合成请求字符串
     * @param queryParamsMap   所有请求参数
     * @return 规范化的请求字符串
     */
    private String canonicalizedQuery(Map queryParamsMap) {
        String[] sortedKeys = queryParamsMap.keySet().toArray(new String[] {});
        Arrays.sort(sortedKeys);
        String queryString = null;
        try {
            StringBuilder canonicalizedQueryString = new StringBuilder();
            for (String key : sortedKeys) {
                canonicalizedQueryString.append("&")
                    .append(percentEncode(key)).append("=")
                    .append(percentEncode(queryParamsMap.get(key)));
            }
            queryString = canonicalizedQueryString.toString().substring(1);
        } catch (UnsupportedEncodingException e) {
            logger.error("UTF-8 encoding is not supported.");
            e.printStackTrace();
        }
        return queryString;
    }

    /***
     * 构造签名字符串
     * @param method       HTTP请求的方法
     * @param urlPath      HTTP请求的资源路径
     * @param queryString  规范化的请求字符串
     * @return 签名字符串
     */
    private String createStringToSign(String method, String urlPath, String queryString) {
        String stringToSign = null;
        try {
            StringBuilder strBuilderSign = new StringBuilder();
            strBuilderSign.append(method);
            strBuilderSign.append("&");
            strBuilderSign.append(percentEncode(urlPath));
            strBuilderSign.append("&");
            strBuilderSign.append(percentEncode(queryString));
            stringToSign = strBuilderSign.toString();
        } catch (UnsupportedEncodingException e) {
            logger.error("UTF-8 encoding is not supported.");
            e.printStackTrace();
        }
        return stringToSign;
    }

    /***
     * 计算签名
     * @param stringToSign      签名字符串
     * @param accessKeySecret   阿里云AccessKey Secret加上与号&
     * @return 计算得到的签名
     */
    private String sign(String stringToSign, String accessKeySecret) {
        try {
            Mac mac = Mac.getInstance(ALGORITHM_NAME);
            mac.init(new SecretKeySpec(
                accessKeySecret.getBytes(ENCODING),
                ALGORITHM_NAME
            ));
            byte[] signData = mac.doFinal(stringToSign.getBytes(ENCODING));
            String signBase64 = DatatypeConverter.printBase64Binary(signData);
            String signUrlEncode = percentEncode(signBase64);
            return signUrlEncode;
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException(e.toString());
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException(e.toString());
        } catch (InvalidKeyException e) {
            throw new IllegalArgumentException(e.toString());
        }
    }

    /***
     * 发送HTTP GET请求,获取token和有效期时间戳
     * @param queryString 请求参数
     */
    private void processGETRequest(String queryString) {
        /**
         * 设置HTTP GET请求
         * 1. 使用HTTP协议
         * 2. Token服务域名
         * 3. 请求路径:/
         * 4. 设置请求参数
         */
        String url = "http://" + this.domain;
        url = url + "/";
        url = url + "?" + queryString;
        Request request = new Request.Builder()
            .url(url)
            .header("Accept", "application/json")
            .get()
            .build();
        try {
            OkHttpClient client = new OkHttpClient();
            Response response = client.newCall(request).execute();
            String result = response.body().string();
            if (response.isSuccessful()) {
                JSONObject rootObj = JSON.parseObject(result);
                JSONObject tokenObj = rootObj.getJSONObject("Token");
                if (tokenObj != null) {
                    this.token = tokenObj.getString("Id");
                    this.expireTime = tokenObj.getLongValue("ExpireTime");
                } else {
                    logger.error("Create the token failed: " + result);
                }
            } else {
                System.err.println("Create the token failed: " + result);
            }
            response.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 向服务端申请访问令牌,调用即返回,任务在后台运行,申请成功后会更新token和expireTime
     *
     * @throws IOException https调用出错
     */
    public void apply() throws IOException {
        Map queryParamsMap = new HashMap();
        queryParamsMap.put("AccessKeyId", this.accessKeyId);
        queryParamsMap.put("Action", this.action);
        queryParamsMap.put("Version", this.version);
        queryParamsMap.put("RegionId", this.regionId);
        queryParamsMap.put("Timestamp", getISO8601Time(null));
        queryParamsMap.put("Format", "JSON");
        queryParamsMap.put("SignatureMethod", "HMAC-SHA1");
        queryParamsMap.put("SignatureVersion", "1.0");
        queryParamsMap.put("SignatureNonce", getUniqueNonce());

        String queryString = canonicalizedQuery(queryParamsMap);
        if (null == queryString) {
            logger.error("create the canonicalized query failed");
            return;
        }

        String method = "GET";
        String urlPath = "/";
        String stringToSign = createStringToSign(method, urlPath, queryString);
        if (null == stringToSign) {
            logger.error("create the sign string failed");
            return;
        }

        String signature = sign(stringToSign, accessKeySecret + "&");
        if (null == signature) {
            logger.error("computer the sign failed!");
            return;
        }
        String queryStringWithSign = "Signature=" + signature + "&" + queryString;
        processGETRequest(queryStringWithSign);
    }

    public String getToken() {
        return token;
    }

    public long getExpireTime() {
        return expireTime;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy