public class CloudAuth {

    /**
     * @param request AipRequest Object
     * @param ak access key ID
     * @param sk secret access key
     * @param timestamp UTC timestamp
     * @return signed authorization header for cloud auth
     */
    public static String sign(AipRequest request, String ak, String sk, String timestamp) {
        HashMap<String, String> headers = request.getHeaders();
        HashMap<String, String> params = request.getParams();
        String httpMethod = request.getHttpMethod().toString();
        String path = request.getUri().getPath();
        // 1. 生成signingKey
        //  1.1 authString,格式为：bce-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds}
        String authStringPrefix = String.format("bce-auth-v1/%s/%s/%d",
                ak, timestamp, AipClientConst.BCE_AUTH_EXPIRE_IN_SECONDS);

        try {
            // 1.2.使用authStringPrefix加上SK，用SHA-256生成sign key
            String signingKey = SignUtil.hmacSha256(sk, authStringPrefix);

            // 2. 生成规范化uri
            String canonicalUri = getCanonicalUri(path);

            // 3. 生成规范化query string
            String canonicalQuery = getCanonicalQuery(params);

            // 4. 生成规范化headers
            String canonicalHeaders = getCanonicalHeaders(headers);

            ArrayList<String> canonicalRequest = new ArrayList<String>();
            canonicalRequest.add(httpMethod);
            canonicalRequest.add(canonicalUri);
            canonicalRequest.add(canonicalQuery);
            canonicalRequest.add(canonicalHeaders);

            String signature = SignUtil.hmacSha256(signingKey, Util.mkString(canonicalRequest.iterator(), '\n'));

            return String.format("%s/%s/%s", authStringPrefix, "", signature);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private static String getCanonicalUri(String path) {
        if (!path.startsWith("/")) {
            path = String.format("/%s", path);
        }
        return Util.uriEncode(path, false);
    }

    private static String getCanonicalQuery(HashMap<String, String> params) {
        if (params.isEmpty()) {
            return "";
        }

        TreeSet<String> querySet = new TreeSet<String>();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (!entry.getKey().toLowerCase().equals("authorization")) {
                querySet.add(String.format("%s=%s",
                        Util.uriEncode(entry.getKey(), true),
                        Util.uriEncode(entry.getValue(), true)));
            }
        }

        return Util.mkString(querySet.iterator(), '&');

    }

    /*
     *   对部分header名称及参数进行编码，默认使用：
     *   1. host
     *   2. content-md5
     *   3. content-length
     *   4. content-type
     *   5. 所有以x-bce-开头的header项
     */
    private static String getCanonicalHeaders(HashMap<String, String> headers) {
        if (headers.isEmpty()) {
            return "";
        }
        TreeSet<String> headerSet = new TreeSet<String>();
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            String key = entry.getKey().trim().toLowerCase();
            if (key.startsWith(AipClientConst.BCE_PREFIX)
                    || AipClientConst.BCE_HEADER_TO_SIGN.contains(key)) {
                headerSet.add(String.format("%s:%s", Util.uriEncode(key, true),
                        Util.uriEncode(entry.getValue().trim(), true)));
            }
        }

        return Util.mkString(headerSet.iterator(), '\n');
    }

} public class DevAuth {

    /**
     * get access_token from openapi
     * @param apiKey API key from console
     * @param secretKey Secret Key from console
     * @param config network config settings
     * @return JsonObject of response from OAuth server
     */
    public static JSONObject oauth(String apiKey, String secretKey, AipClientConfiguration config) {
        try {
            AipRequest request = new AipRequest();
            request.setUri(new URI(AipClientConst.OAUTH_URL));
            request.addBody("grant_type", "client_credentials");
            request.addBody("client_id", apiKey);
            request.addBody("client_secret", secretKey);
            request.setConfig(config);
            int statusCode = 500;
            AipResponse response = null;
            // add retry
            int cnt = 0;
            while (statusCode == 500 && cnt < 3) {
                response = AipHttpClient.post(request);
                statusCode = response.getStatus();
                cnt++;
            }
            String res = response.getBodyStr();
            if (res != null && !res.equals("")) {
                return new JSONObject(res);
            } else {
                return Util.getGeneralError(statusCode, "Server response code: " + statusCode);
            }
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        return Util.getGeneralError(-1, "unknown error");
    }
} public enum AipError {
    SUCCESS("0", "Success"),
    IMAGE_SIZE_ERROR("SDK100", "image size error"),
    IMAGE_LENGTH_ERROR("SDK101", "image length error"),
    IMAGE_READ_ERROR("SDK102", "read image file error"),
    USER_INFO_SIZE_ERROR("SDK103", "user_info size error"),
    GROUP_ID_FORMAT_ERROR("SDK104", "group_id format error"),
    GROUP_ID_SIZE_ERROR("SDK105", "group_id size error"),
    UID_FORMAT_ERROR("SDK106", "uid format error"),
    UID_SIZE_ERROR("SDK107", "uid size error"),
    NET_TIMEOUT_ERROR("SDK108", "connection or read data time out"),
    UNSUPPORTED_IMAGE_FORMAT_ERROR("SDK109", "unsupported image format"),
    ILLEGAL_REQUEST_ID_ERROR("SDK110", "illegal request id found: "),     // 填充具体id
    ASYNC_TIMEOUT_ERROR("SDK111", "wait for aysnc result timeout"),
    DOWNLOAD_FILE_ERROR("SDK112", "download file failed");

    private final String errorCode;
    private final String errorMsg;

    AipError(String errorCode, String errorMsg) {
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

    public String getErrorCode() {
        return errorCode;
    }

    public String getErrorMessage() {
        return errorMsg;
    }

    public JSONObject toJsonResult() {
        JSONObject json = new JSONObject();
        json.put("error_code", errorCode);
        json.put("error_msg", errorMsg);
        return json;
    }

    @Override
    public String toString() {
        return toJsonResult().toString();
    }
} public class AipHttpClient {


    /**
     * post方式请求服务器(https协议)
     *
     * @param request 请求内容
     * @return AipResponse
     */
    public static AipResponse post(AipRequest request) {
        String url;
        String charset = request.getContentEncoding();
        String content = request.getBodyStr();
        HashMap<String, String> header = request.getHeaders();
        AipResponse response = new AipResponse();

        DataOutputStream out = null;
        InputStream is = null;
        try {
            if (request.getParams().isEmpty()) {
                url = request.getUri().toString();
            }
            else {
                url = String.format("%s?%s", request.getUri().toString(), request.getParamStr());
            }

            URL console = new URL(url);
            Proxy proxy = request.getConfig() == null ? Proxy.NO_PROXY : request.getConfig().getProxy();
            HttpURLConnection conn = (HttpURLConnection) console.openConnection(proxy);

            // set timeout
            if (request.getConfig() != null) {
                conn.setConnectTimeout(request.getConfig().getConnectionTimeoutMillis());
                conn.setReadTimeout(request.getConfig().getSocketTimeoutMillis());
            }
            conn.setDoOutput(true);
            // 添加header
            for (Map.Entry<String, String> entry : header.entrySet()) {
                conn.setRequestProperty(entry.getKey(), entry.getValue());
            }

            conn.connect();
            out = new DataOutputStream(conn.getOutputStream());
            out.write(content.getBytes(charset));
            out.flush();
            int statusCode = conn.getResponseCode();
            response.setHeader(conn.getHeaderFields());
            response.setStatus(statusCode);
            response.setCharset(charset);
            if (statusCode != 200) {
                return response;
            }

            is = conn.getInputStream();
            if (is != null) {
                ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = is.read(buffer)) != -1) {
                    outStream.write(buffer, 0, len);
                }
                response.setBody(outStream.toByteArray());
            }
            return response;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return response;
    }

} public class AipRequest {
    private HashMap<String, String> headers;
    private HashMap<String, String> params;
    private HashMap<String, Object> body;
    private URI uri;
    private HttpMethodName httpMethod;
    private EBodyFormat bodyFormat;
    private String contentEncoding;
    private AipClientConfiguration config;


    public AipRequest() {
        headers = new HashMap<String, String>();
        params = new HashMap<String, String>();
        body = new HashMap<String, Object>();
        httpMethod = HttpMethodName.POST;
        bodyFormat = EBodyFormat.FORM_KV;
        contentEncoding = HttpCharacterEncoding.DEFAULT_ENCODING;
        config = null;
    }

    public AipRequest(HashMap<String, String> header, HashMap<String, String> bodyParams) {
        headers = header;
        params = bodyParams;
    }

    public String getContentEncoding() {
        return contentEncoding;
    }

    public void setContentEncoding(String contentEncoding) {
        this.contentEncoding = contentEncoding;
    }

    public EBodyFormat getBodyFormat() {
        return bodyFormat;
    }

    public void setBodyFormat(EBodyFormat bodyFormat) {
        this.bodyFormat = bodyFormat;
    }

    public void addHeader(String key, String value) {
        headers.put(key, value);
        if (key.equals(Headers.CONTENT_ENCODING)) {
            this.contentEncoding = value;
        }
    }

    public void addParam(String key, String value) {
        params.put(key, value);
    }

    public void addBody(String key, Object value) {
        body.put(key, value);
    }

    public void addBody(HashMap other) {
        if (other != null) {
            body.putAll(other);
        }
    }

    public HashMap<String, String> getParams() {
        return params;
    }

    /**
     * get body content depending on bodyFormat
     * @return body content as String
     */
    public String getBodyStr() {
        ArrayList<String> arr = new ArrayList<String>();
        if (bodyFormat.equals(EBodyFormat.FORM_KV)) {
            for (Map.Entry<String, Object> entry : body.entrySet()) {
                if (entry.getValue() == null || entry.getValue().equals("")) {
                    arr.add(Util.uriEncode(entry.getKey(), true));
                } else {
                    arr.add(String.format("%s=%s", Util.uriEncode(entry.getKey(), true),
                            Util.uriEncode(entry.getValue().toString(), true)));
                }
            }
            return Util.mkString(arr.iterator(), '&');
        }
        else if (bodyFormat.equals(EBodyFormat.RAW_JSON)) {
            JSONObject json = new JSONObject();
            for (Map.Entry<String, Object> entry : body.entrySet()) {
                json.put(entry.getKey(), entry.getValue());
            }
            return json.toString();
        }
        else if (bodyFormat.equals(EBodyFormat.RAW_JSON_ARRAY)) {
            return (String) body.get("body");
        }
        return "";
    }

    public String getParamStr() {
        StringBuffer buffer = new StringBuffer();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            buffer.append(String.format("%s=%s&", entry.getKey(), entry.getValue()));
        }
        if (buffer.length() > 0) {
            buffer.deleteCharAt(buffer.length() - 1);
        }
        return buffer.toString();
    }

    public HashMap<String, Object> getBody() {
        return body;
    }

    public void setBody(HashMap<String, Object> body) {
        this.body = body;
    }

    public void setParams(HashMap<String, String> params) {
        this.params = params;
    }

    public HashMap<String, String> getHeaders() {
        return headers;
    }

    public void setHeaders(HashMap<String, String> headers) {
        this.headers = headers;
    }

    public URI getUri() {
        return uri;
    }

    public void setUri(URI uri) {
        this.uri = uri;
    }

    public void setUri(String url) {
        try {
            this.uri = new URI(url);
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
    }

    public HttpMethodName getHttpMethod() {
        return httpMethod;
    }

    public void setHttpMethod(HttpMethodName httpMethod) {
        this.httpMethod = httpMethod;
    }

    public AipClientConfiguration getConfig() {
        return config;
    }

    public void setConfig(AipClientConfiguration config) {
        this.config = config;
    }
} public class AipResponse {

    private Map<String, List<String>> header;
    private byte[] body;
    private String charset;
    private int status;

    public AipResponse() {
        status = 0;
        charset = "UTF-8";
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getBodyStr() {
        if (body == null) {
            return "";
        }
        try {
            return new String(body, charset);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return new String(body);
        }
    }

    public Map<String, List<String>> getHeader() {
        return header;
    }

    public void setHeader(Map<String, List<String>> header) {
        this.header = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
        for (Map.Entry<String, List<String>> entry: header.entrySet()) {
            if (entry.getKey() != null) {
                this.header.put(entry.getKey(), entry.getValue());
            }
        }
    }

    public byte[] getBody() {
        return body;
    }

    public void setBody(byte[] body) {
        this.body = body;
    }

    public String getCharset() {
        return charset;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }
} public interface Headers {

    /*
     * Standard HTTP Headers
     */

    public static final String AUTHORIZATION = "Authorization";

    public static final String CACHE_CONTROL = "Cache-Control";

    public static final String CONTENT_DISPOSITION = "Content-Disposition";

    public static final String CONTENT_ENCODING = "Content-Encoding";

    public static final String CONTENT_LENGTH = "Content-Length";

    public static final String CONTENT_MD5 = "Content-MD5";

    public static final String CONTENT_RANGE = "Content-Range";

    public static final String CONTENT_TYPE = "Content-Type";

    public static final String DATE = "Date";

    public static final String ETAG = "ETag";

    public static final String EXPIRES = "Expires";

    public static final String HOST = "Host";

    public static final String LAST_MODIFIED = "Last-Modified";

    public static final String RANGE = "Range";

    public static final String SERVER = "Server";

    public static final String TRANSFER_ENCODING = "Transfer-Encoding";

    public static final String USER_AGENT = "User-Agent";


    /*
     * BCE Common HTTP Headers
     */

    public static final String BCE_ACL = "x-bce-acl";

    public static final String BCE_CONTENT_SHA256 = "x-bce-content-sha256";

    public static final String BCE_COPY_METADATA_DIRECTIVE = "x-bce-metadata-directive";

    public static final String BCE_COPY_SOURCE = "x-bce-copy-source";

    public static final String BCE_COPY_SOURCE_IF_MATCH = "x-bce-copy-source-if-match";

    public static final String BCE_DATE = "x-bce-date";

    public static final String BCE_DEBUG_ID = "x-bce-debug-id";

    public static final String BCE_PREFIX = "x-bce-";

    public static final String BCE_REQUEST_ID = "x-bce-request-id";

    public static final String BCE_SECURITY_TOKEN = "x-bce-security-token";

    public static final String BCE_STORAGE_CLASS = "x-bce-storage-class";

    public static final String BCE_USER_METADATA_PREFIX = "x-bce-meta-";

} public interface HttpContentType {

    public static final String FORM_DATA = "multipart/form-data";
    public static final String JSON_DATA = "application/json";
    public static final String FORM_URLENCODE_DATA = "application/x-www-form-urlencoded";
} public enum HttpMethodName {
    GET, POST, PUT, DELETE, HEAD;
} public class AipClientConfiguration {

    // 连接超时设置
    private int connectionTimeoutMillis;
    private int socketTimeoutMillis;
    private Proxy proxy;

    public AipClientConfiguration() {
        this.connectionTimeoutMillis = 0;
        this.socketTimeoutMillis = 0;
        this.proxy = Proxy.NO_PROXY;
    }

    public AipClientConfiguration(int connectionTimeoutMillis, int socketTimeoutMillis, Proxy proxy) {
        this.connectionTimeoutMillis = connectionTimeoutMillis;
        this.socketTimeoutMillis = socketTimeoutMillis;
        this.proxy = proxy;
    }

    public int getConnectionTimeoutMillis() {
        return connectionTimeoutMillis;
    }

    public void setConnectionTimeoutMillis(int connectionTimeoutMillis) {
        this.connectionTimeoutMillis = connectionTimeoutMillis;
    }

    public int getSocketTimeoutMillis() {
        return socketTimeoutMillis;
    }

    public void setSocketTimeoutMillis(int socketTimeoutMillis) {
        this.socketTimeoutMillis = socketTimeoutMillis;
    }

    public Proxy getProxy() {
        return proxy;
    }

    public void setProxy(Proxy proxy) {
        this.proxy = proxy;
    }

    public void setProxy(String host, int port, Proxy.Type type) {
        SocketAddress addr = new InetSocketAddress(host, port);
        this.proxy = new Proxy(type, addr);
    }
} public class AipClientConst {

    // for cloudAuth
    public static final Integer BCE_AUTH_EXPIRE_IN_SECONDS = 1800;  // 30min
    public static final HashSet<String> BCE_HEADER_TO_SIGN =
            new HashSet<String>(Arrays.asList("host", "content-md5", "content-length", "content-type"));
    public static final String BCE_PREFIX = "x-bce-";

    // for openapi
    public static final String OAUTH_URL = "https://aip.baidubce.com/oauth/2.0/token";

    // encoding
    public static final String DEFAULT_ENCODING = "UTF8";
    public static final String ENCODING_GBK = "GBK";

    // openAPI access right
    public static final List<String> AI_ACCESS_RIGHT =
            Arrays.asList(
                    "audio_voice_assistant_get",            // speech
                    "audio_tts_post",
                    "brain_all_scope");


    public static final Integer IAM_ERROR_CODE = 14;
    public static final Integer OPENAPI_NO_ACCESS_ERROR_CODE = 6;
    public static final String OPENAPI_NO_ACCESS_ERROR_MSG = "No permission to access data";

    public static final String LOG4J_CONF_PROPERTY = "aip.log4j.conf";
} public class SignUtil {
    private static final char[] DIGITS;

    public static String hmacSha256(String key, String data) throws AipException {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(),
                    mac.getAlgorithm());
            mac.init(signingKey);
            return encodeHex(mac.doFinal(data.getBytes()));
        } catch (Exception e) {
            e.printStackTrace();
            throw new AipException(-1, "Fail to generate HMAC-SHA256 signature");
        }
    }

    public static String md5(String data, String charset) {
        try {
            byte[] msg = data.getBytes(charset);
            MessageDigest md = MessageDigest.getInstance("MD5");
            return encodeHex(md.digest(msg));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String encodeHex(byte[] data) {
        int l = data.length;
        char[] out = new char[l << 1];
        int i = 0;

        for (int j = 0; i < l; ++i) {
            out[j++] = DIGITS[(240 & data[i]) >>> 4];
            out[j++] = DIGITS[15 & data[i]];
        }

        return new String(out);
    }

    static {
        DIGITS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
    }
} public class Util {

    private static BitSet URI_UNRESERVED_CHARACTERS = new BitSet();
    private static String[] PERCENT_ENCODED_STRINGS = new String[256];


    static {
        for (int i = 'a'; i <= 'z'; i++) {
            URI_UNRESERVED_CHARACTERS.set(i);
        }
        for (int i = 'A'; i <= 'Z'; i++) {
            URI_UNRESERVED_CHARACTERS.set(i);
        }
        for (int i = '0'; i <= '9'; i++) {
            URI_UNRESERVED_CHARACTERS.set(i);
        }
        URI_UNRESERVED_CHARACTERS.set('-');
        URI_UNRESERVED_CHARACTERS.set('.');
        URI_UNRESERVED_CHARACTERS.set('_');
        URI_UNRESERVED_CHARACTERS.set('~');

        for (int i = 0; i < PERCENT_ENCODED_STRINGS.length; ++i) {
            PERCENT_ENCODED_STRINGS[i] = String.format("%%%02X", i);
        }

    }

    public static String mkString(Iterator<String> iter, char seprator) {
        if (!iter.hasNext()) {
            return "";
        }
        StringBuilder builder = new StringBuilder();
        while (iter.hasNext()) {
            String item = iter.next();
            builder.append(item);
            builder.append(seprator);
        }

        builder.deleteCharAt(builder.length() - 1);     // remove last sep
        return builder.toString();
    }


    /**
     * Normalize a string for use in BCE web service APIs. The normalization algorithm is:
     * <ol>
     *   <li>Convert the string into a UTF-8 byte array.</li>
     *   <li>Encode all octets into percent-encoding, except all URI unreserved characters per the RFC 3986.</li>
     * </ol>
     *
     * All letters used in the percent-encoding are in uppercase.
     *
     * @param value the string to normalize.
     * @param encodeSlash if encode '/'
     * @return the normalized string.
     */
    public static String uriEncode(String value, boolean encodeSlash) {
        try {
            StringBuilder builder = new StringBuilder();
            for (byte b : value.getBytes(AipClientConst.DEFAULT_ENCODING)) {
                if (URI_UNRESERVED_CHARACTERS.get(b & 0xFF)) {
                    builder.append((char) b);
                } else {
                    builder.append(PERCENT_ENCODED_STRINGS[b & 0xFF]);
                }
            }
            String encodeString = builder.toString();
            if (!encodeSlash) {
                return encodeString.replace("%2F", "/");
            }
            return encodeString;
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    public static String getCanonicalTime() {
        SimpleDateFormat utcDayFormat = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat utcHourFormat = new SimpleDateFormat("hh:mm:ss");
        utcDayFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        utcHourFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date now = new Date();
        return String.format("%sT%sZ", utcDayFormat.format(now), utcHourFormat.format(now));
    }

    /**
     *
     * @param filePath 文件路径
     * @return file bytes
     * @throws IOException 读取文件错误
     */

    public static byte[] readFileByBytes(String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new FileNotFoundException(filePath);
        }

        ByteArrayOutputStream bos = new ByteArrayOutputStream(((int) file.length()));
        BufferedInputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(file));
            int bufSize = 1024;
            byte[] buffer = new byte[bufSize];
            int len = 0;
            while (-1 != (len = in.read(buffer, 0, bufSize))) {
                bos.write(buffer, 0, len);
            }
            return bos.toByteArray();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            bos.close();
        }
    }

    public static void writeBytesToFileSystem(byte[] data, String output) throws IOException {
        DataOutputStream out = null;
        try {
            out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(output)));
            out.write(data);
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

    public static JSONObject getGeneralError(int errorCode, String errorMsg) {
        JSONObject json = new JSONObject();
        json.put("error_code", errorCode);
        json.put("error_msg", errorMsg);
        return json;
    }

    public static boolean isLiteral(String input) {
        Pattern pattern = Pattern.compile("[0-9a-zA-Z_]*");
        return pattern.matcher(input).matches();
    }
}
import com.baidu.aip.auth.CloudAuth;
import com.baidu.aip.auth.DevAuth;
import com.baidu.aip.error.AipError;
import com.baidu.aip.http.AipHttpClient;
import com.baidu.aip.http.AipRequest;
import com.baidu.aip.http.AipResponse;
import com.baidu.aip.http.Headers;
import com.baidu.aip.http.HttpContentType;
import com.baidu.aip.http.HttpMethodName;
import com.baidu.aip.util.AipClientConfiguration;
import com.baidu.aip.util.AipClientConst;
import com.baidu.aip.util.SignUtil;
import com.baidu.aip.util.Util;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.net.Proxy;
import java.util.Calendar;
import java.util.concurrent.atomic.AtomicBoolean;
        state = new AuthState();
    }

    /**
     *
     * @param timeout 服务器建立连接的超时时间（单位：毫秒）
     */
    public void setConnectionTimeoutInMillis(int timeout) {
        if (config == null) {
            config = new AipClientConfiguration();
        }
        this.config.setConnectionTimeoutMillis(timeout);
    }

    /**
     *
     * @param timeout 通过打开的连接传输数据的超时时间（单位：毫秒）
     */
    public void setSocketTimeoutInMillis(int timeout) {
        if (config == null) {
            config = new AipClientConfiguration();
        }
        this.config.setSocketTimeoutMillis(timeout);
    }

    /**
     * 设置访问网络需要的http代理
     * @param host 代理服务器地址
     * @param port 代理服务器端口
     */
    public void setHttpProxy(String host, int port) {
        if (config == null) {
            config = new AipClientConfiguration();
        }
        this.config.setProxy(host, port, Proxy.Type.HTTP);
    }

    /**
     * 设置访问网络需要的socket代理
     * @param host 代理服务器地址
     * @param port 代理服务器端口
     */
    public void setSocketProxy(String host, int port) {
        if (config == null) {
            config = new AipClientConfiguration();
        }
        this.config.setProxy(host, port, Proxy.Type.SOCKS);
    }

    /**
     * get OAuth access token, synchronized function
     * @param config 网络连接设置
     */
    protected synchronized void getAccessToken(AipClientConfiguration config) {
        if (!needAuth()) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(String.format("app[%s] no need to auth", this.appId));
            }
            return;
        }
        JSONObject res = DevAuth.oauth(aipKey, aipToken, config);
        if (res == null) {
            LOGGER.warn("oauth get null response");
            return;
        }
        if (!res.isNull("access_token")) {
            // openAPI认证成功
            state.transfer(true);
            accessToken = res.getString("access_token");

            LOGGER.info("get access_token success. current state: " + state.toString());
            Integer expireSec = res.getInt("expires_in");
            Calendar c = Calendar.getInstance();
            c.add(Calendar.SECOND, expireSec);
            expireDate = c;
            // isBceKey.set(false);
            // 验证接口权限
            String[] scope = res.getString("scope").split(" ");
            boolean hasRight = false;
            for (String str : scope) {
                if (AipClientConst.AI_ACCESS_RIGHT.contains(str)) {
                    // 权限验证通过
                    hasRight = true;
                    break;
                }
            }
            state.transfer(hasRight);
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("current state after check priviledge: " + state.toString());
            }
        }
        else if (!res.isNull("error_code")) {
            state.transfer(false);
            LOGGER.warn("oauth get error, current state: " + state.toString());
        }
    }

    /*
     *   需要重新获取access_token的条件：
     *   1. 是DEV用户，即 isBceKey为false
     *   2. isAuthorized为false，或isAuthorized为true，但当前时间晚于expireDate前一天
     */
    protected Boolean needAuth() {
        if (isBceKey.get()) {
            return false;
        }
        Calendar c = Calendar.getInstance();
        c.add(Calendar.DATE, 1);
        return !isAuthorized.get() || c.after(expireDate);
    }

    /*
     *  为DEV创建的用户填充body
     */
    protected void preOperation(AipRequest request) {
        if (needAuth()) {
            getAccessToken(config);
        }

        request.setHttpMethod(HttpMethodName.POST);
        request.addHeader(Headers.CONTENT_TYPE, HttpContentType.FORM_URLENCODE_DATA);