From c928752bbbb856c90a622f4824861ef3aab8779a Mon Sep 17 00:00:00 2001 From: Zhong Lufan Date: Mon, 20 Jun 2022 00:10:44 +0800 Subject: [PATCH] Rewrite call method --- AliyunOpenApiSDK.sh | 131 ++++++++++++++++++++------------------ README.md | 40 ++++-------- examples/UpdateSSLCert.sh | 56 +++------------- 3 files changed, 91 insertions(+), 136 deletions(-) diff --git a/AliyunOpenApiSDK.sh b/AliyunOpenApiSDK.sh index 6a620e0..adf11ca 100644 --- a/AliyunOpenApiSDK.sh +++ b/AliyunOpenApiSDK.sh @@ -8,74 +8,76 @@ for _command in openssl curl; do done unset _command -declare AliAccessKeyId AliAccessKeySecret -_AliAccessKeyId=$AliAccessKeyId -_AliAccessKeySecret=$AliAccessKeySecret - -# aliapi_rpc [api_custom_key] [api_custom_value] +# aliapi_rpc [<--key> ...] aliapi_rpc() { - _AliAccessKeyId=$AliAccessKeyId - _AliAccessKeySecret=$AliAccessKeySecret - if [[ -z $_AliAccessKeyId ]]; then - echo "Aliyun OpenAPI SDK: 'AliAccessKeyId' environment variable not found or null" - return 61 - fi - if [[ -z $_AliAccessKeySecret ]]; then - echo "Aliyun OpenAPI SDK: 'AliAccessKeySecret' environment variable not found or null" - return 62 + if [[ ! -v AliAccessKeyId || ! -v AliAccessKeySecret ]]; then + echo "Aliyun OpenAPI SDK: 'AliAccessKeyId' or 'AliAccessKeySecret' environment variable not found" >&2 + return 3 fi - if ! [[ $# -eq 4 || $# -eq 6 ]];then - echo "Aliyun OpenAPI SDK: aliapi_rpc() not enough parameters" - return 66 + if [[ $# -lt 4 ]];then + echo "Aliyun OpenAPI SDK: aliapi_rpc() not enough parameters" >&2 + return 2 fi - local _http_host=$1 _http_method=$2 _api_action=$4 _api_version=$3 + + local _AliAccessKeyId=$AliAccessKeyId _AliAccessKeySecret=$AliAccessKeySecret + + local _http_method=$1 # 兼容 BusyBox # shellcheck disable=SC2018,SC2019 _http_method=$(tr "a-z" "A-Z" <<< "$_http_method") - # 公共查询参数键 - local _api_common_key=( - "AccessKeyId" - "Action" - "Format" - "SignatureMethod" - "SignatureVersion" - "SignatureNonce" - "Timestamp" - "Version" + shift + local _http_host=$1 + shift + local _api_version=$1 + shift + local _api_action=$1 + shift + + local -A _api_params + _api_params=( + ["AccessKeyId"]=$_AliAccessKeyId + ["Action"]=$_api_action + ["Format"]="JSON" + ["SignatureMethod"]="HMAC-SHA1" + ["SignatureVersion"]="1.0" + ["SignatureNonce"]=$(_aliapi_signature_nonce) + ["Timestamp"]=$(_aliapi_timestamp_rpc) + ["Version"]=$_api_version ) - # 公共查询参数值 - local _ali_common_value=( - "$_AliAccessKeyId" - "$_api_action" - "JSON" - "HMAC-SHA1" - "1.0" - "$(_ali_signature_nonce)" - "$(_ali_timestamp_rpc)" - "$_api_version" - ) - declare -a _ali_custom_key _ali_custom_value _ali_key _ali_value - # 自定义查询参数键值 - read -r -a _ali_custom_key <<< "$5" - read -r -a _ali_custom_value <<< "$6" - # 合并查询键值 - read -r -a _ali_key <<< "${_api_common_key[*]} ${_ali_custom_key[*]}" - read -r -a _ali_value <<< "${_ali_common_value[*]} ${_ali_custom_value[*]}" + # 解析其余参数 + while [[ $# -ne 0 ]] + do + case $1 in + --*) + if [[ $# -le 1 ]]; then + echo "Aliyun OpenAPI SDK: aliapi_rpc() '$1' has no value" >&2 + return 2 + fi + _api_params[${1:2}]="$2" + shift + shift + ;; + *) + echo "Aliyun OpenAPI SDK: aliapi_rpc() Unknown parameter: $1" >&2 + return 2 + ;; + esac + done + local _query_str="" local _key _value - local i - for (( i = 0; i < ${#_ali_key[@]}; ++i )); do - _key=${_ali_key[$i]} - _value=${_ali_value[$i]} + for _key in "${!_api_params[@]}"; do + _value=${_api_params[$_key]} # 参数值如果是以 () 结束,代表需要执行函数获取值,如果函数不存在,使用原始值。 [[ ($(grep -E "^.+\(\)$" <<< "$_value") == "$_value" && $(type -t "${_value:0:-2}") == "function") ]] && _value=$(${_value:0:-2}) - _value=$(_urlencode "$_value") + _value=$(_aliapi_urlencode "$_value") _query_str+="$_key=$_value&" done - local _ali_signature_value - _ali_signature_value=$(_ali_signature_rpc "$_http_method" "$_query_str") - _query_str+="Signature=$(_urlencode "$_ali_signature_value")" + + local _signature + _signature=$(_aliapi_signature_rpc "$_http_method" "$_query_str") + _query_str+="Signature=$(_aliapi_urlencode "$_signature")" local _curl_out _http_code _http_url="https://$_http_host/?$_query_str" _curl_out=$(mktemp) _http_code=$(curl --location --silent --show-error --request "$_http_method" --output "$_curl_out" --write-out "%{http_code}" --connect-timeout 3 "$_http_url") && cat "$_curl_out" - <<< "" @@ -83,11 +85,11 @@ aliapi_rpc() { [[ $_http_code -eq 200 ]] && return 0 || return 1 } -_ali_signature_rpc() { +_aliapi_signature_rpc() { local _http_method=$1 _str _query_str _sign_str - _str=$(echo -n "$2" | tr "&" "\n" | sort) + _str=$(LC_ALL=C echo -n "$2" | tr "&" "\n" | sort) _query_str=$(echo -n "$_str" | tr "\n" "&") - _sign_str="$_http_method&$(_urlencode "/")&$(_urlencode "$_query_str")" + _sign_str="$_http_method&$(_aliapi_urlencode "/")&$(_aliapi_urlencode "$_query_str")" echo -n "$_sign_str" | openssl sha1 -hmac "$_AliAccessKeySecret&" -binary | openssl base64 -e } @@ -96,14 +98,21 @@ _aliapi_timestamp_rpc() { date -u -Iseconds } -_ali_signature_nonce() { - date "+%s%N" +_aliapi_signature_nonce() { } -_urlencode() { +_aliapi_urlencode() { local result result=$(curl --get --silent --output /dev/null --write-out "%{url_effective}" --data-urlencode "=$1" "") + result="${result//+/%20}" # 替换 + 为 %20 echo "${result#*\?}" } -[[ $# -ne 0 ]] && aliapi_rpc "$@" +if [[ ${#BASH_SOURCE[@]} -eq 1 ]]; then + set -euf -o pipefail + if [[ $# -eq 0 ]]; then + echo "$0 [<--key> ...]" >&2 + exit 2 + fi + aliapi_rpc "$@" +fi diff --git a/README.md b/README.md index 2eef355..2f0e751 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,10 @@ -# Aliyun OpenAPI Shell SDK +# Aliyun OpenAPI Bash SDK -这是一个非官方的阿里云 OpenAPI Shell SDK,方便 Shell 脚本调用阿里云 OpenAPI,SDK 主要实现了自动计算 OpenAPI 的请求签名。 - -虽然阿里云官方有 [AliyunCLI](https://github.com/aliyun/aliyun-cli),可以在 Shell 环境下使用阿里云 OpenAPI,但是并不支持某些 API (比如 SSL 证书)。 -对于存储空间有限的嵌入式设备,Shell SDK 可能是更好的选择。 +这是一个非官方的阿里云 OpenAPI Bash SDK,方便 Bash 脚本调用阿里云 OpenAPI,SDK 主要实现了自动计算 OpenAPI 的请求签名。 理论上支持所有阿里云 RPC OpenAPI,暂不支持 RESTful OpenAPI,将来可能会支持。 -> 这可能是最好用的 Aliyun OpenAPI Shell SDK +> 这可能是最好用的 Aliyun OpenAPI Bash SDK ## 依赖 @@ -25,11 +22,10 @@ ```bash # Output: JsonString # Retrun Code: 0 = HTTP_STATUS_CODE == 200 | 1 = HTTP_STATUS_CODE != 200 -aliapi_rpc [api_custom_key] [api_custom_value] -# api_custom_key 和 api_custom_value 可以省略,但不允许只传递其中一个。 +aliapi_rpc [<--key> ...] ``` -PS: `AliyunOpenApiSDK.sh` 支持独立执行,参数与函数参数一致。 +PS: `AliyunOpenApiSDK.sh` 支持作为脚本独立执行,脚本参数与函数参数一致。 **示例:** @@ -43,29 +39,17 @@ export AliAccessKeySecret="" # 导入 SDK source AliyunOpenApiSDK.sh -# 自定义请求参数的键值数组顺序要一一对应,数组成员不能包含空格。 -# 自定义值支持自定义函数,如果你需要包含空格或者读取文件等操作,可以声明一个自定义函数,像下面这样。 -# 如果自定义值数组成员以 () 结尾,SDK 在获取值的时候会判断自定义函数是否存在并执行,如果不存在则使用原始值。 - -# 自定义请求参数的键 -api_custom_key=( - "CurrentPage" - "ShowSize" -) -# 自定义请求参数的值 -api_custom_value=( - "1" - "get_show_size()" # 解析参数时会执行函数 (所以最后提交的值是 50) -) +# 如果值以 () 结尾,那么 SDK 会假设它是一个已定义函数,获取值时会判断函数是否存在并执行,如果不存在则使用原始值。 get_show_size() { echo 50 } # 获取 SSL 证书列表:https://help.aliyun.com/document_detail/126511.html -aliapi_rpc "cas.aliyuncs.com" "GET" "2018-07-13" "DescribeUserCertificateList" "${api_custom_key[*]}" "${api_custom_value[*]}" +# 解析参数时会执行函数 (所以 ShowSize 的值是 50) +aliapi_rpc GET cas.aliyuncs.com 2018-07-13 DescribeUserCertificateList --CurrentPage 1 --ShowSize "get_show_size()" # $? == 0 代表 HTTP CODE == 200 反之 $? == 1 -# 只要 curl 的返回代码 == 0 就会返回接收到的数据 +# 只要 curl 的退出代码 == 0 就会返回接收到的数据 if [[ $? -eq 0 ]]; then # 执行成功 else @@ -73,8 +57,6 @@ else fi ``` -更多示例请参考 [examples](https://github.com/Hill-98/aliyun-openapi-shell-sdk/tree/master/examples) 下的文件 +更多示例请参考 [examples](https://github.com/Hill-98/aliyun-openapi-bash-sdk/tree/master/examples) 下的文件 -如果你有好的示例,欢迎提交 [PR](https://github.com/Hill-98/aliyun-openapi-shell-sdk/pulls) - -如果你有建议 / BUG 要反馈,请提交 [Issue](https://github.com/Hill-98/aliyun-openapi-shell-sdk/issues) +如果你有好的示例,欢迎提交 [PR](https://github.com/Hill-98/aliyun-openapi-bash-sdk/pulls)。 diff --git a/examples/UpdateSSLCert.sh b/examples/UpdateSSLCert.sh index fa6b94f..96f3355 100644 --- a/examples/UpdateSSLCert.sh +++ b/examples/UpdateSSLCert.sh @@ -22,7 +22,7 @@ ACME_ENV_LIST=( ) # 检查环境变量是否存在 for value in "${ACME_ENV_LIST[@]}" ; do - declare -p "$value" &>/dev/null || exit 1 + [[ ! -v "$value" ]] || exit 1 done unset value @@ -45,58 +45,22 @@ DOMAIN_LIST=( "example.example.com" ) -api_custom_key=( - "CurrentPage" - "ShowSize" -) -api_custom_value=( - "1" - "50" -) # 获取证书列表 -result=$(aliapi_rpc "cas.aliyuncs.com" "GET" "2018-07-13" "DescribeUserCertificateList" "${api_custom_key[*]}" "${api_custom_value[*]}" || exit 101) +result=$(aliapi_rpc GET cas.aliyuncs.com 2018-07-13 DescribeUserCertificateList --CurrentPage 1 --ShowSize 50) || exit 101 # 使用 jq 处理返回的 JSON 数据并提取出匹配当前证书域名的证书列表的 ID,用于稍后的删除旧证书操作。 cert_list=$(jq -cr ".CertificateList|map(select(.common == \"$DOMAIN\"))|map(.id)|.[]" <<< "$result") -api_custom_key=( - "Cert" - "Key" - "Name" -) -# 使用自定义函数获取证书和密钥,保证内容可以被完整的传递。 -api_custom_value=( - "get_cert()" - "get_key()" - "$CERT_NAME" -) # 上传新的证书 -aliapi_rpc "cas.aliyuncs.com" "GET" "2018-07-13" "CreateUserCertificate" "${api_custom_key[*]}" "${api_custom_value[*]}" || exit 102 +aliapi_rpc GET cas.aliyuncs.com 2018-07-13 CreateUserCertificate --Cert "get_cert()" --Key "get_key()" --Name "$CERT_NAME" || exit 102 + # 设置 CDN 域名列表使用新的证书 -for domain in "${DOMAIN_LIST[@]}"; do - api_custom_key=( - "DomainName" - "ServerCertificateStatus" - "CertName" - "CertType" - ) - api_custom_value=( - "$domain" - "on" - "$CERT_NAME" - "cas" - ) - aliapi_rpc "cdn.aliyuncs.com" "GET" "2018-05-10" "SetDomainServerCertificate" "${api_custom_key[*]}" "${api_custom_value[*]}" || exit 103 +for _domain in "${DOMAIN_LIST[@]}"; do + aliapi_rpc GET cdn.aliyuncs.com 2018-05-10 SetDomainServerCertificate --DomainName "$_domain" --ServerCertificateStatus on --CertName "$CERT_NAME" --CertType cas || exit 103 done -unset domain +unset _domain # 删除旧的证书 -for id in ${cert_list}; do - api_custom_key=( - "CertId" - ) - api_custom_value=( - "$id" - ) - aliapi_rpc "cas.aliyuncs.com" "GET" "2018-07-13" "DeleteUserCertificate" "${api_custom_key[*]}" "${api_custom_value[*]}" || exit 104 +for _id in ${cert_list}; do + aliapi_rpc GET cas.aliyuncs.com 2018-07-13 DeleteUserCertificate --CertId "$_id" || exit 104 done -unset id +unset _id