作者:俏巴
概述
阿里云会对每个访问的请求进行身份验证,所以无论使用 HTTP 还是 HTTPS 协议提交请求,都需要在请求中包含签名(Signature)信息。通过使用 Access Key ID 和 Access Key Secret 进行对称加密的方法来验证请求的发送者身份。阿里云提供了多种语言的 SDK 及第三方 SDK,可以免去您对签名算法进行编码的麻烦。您可以从这里了解更多阿里云 SDK 的信息,前面系列博客也结合各种语言的SDK进行了实现。本文以DetectFace API为示例,演示实际基于签名校验的实现。
操作步骤
1、pom.xml
<dependencies>
<span class="xml"><span class="hljs-comment"><!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.httpcomponents<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>httpclient<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">version</span>></span>4.4<span class="hljs-tag"></<span class="hljs-name">version</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-comment"><!-- https://mvnrepository.com/artifact/commons-lang/commons-lang --></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>commons-lang<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>commons-lang<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">version</span>></span>2.6<span class="hljs-tag"></<span class="hljs-name">version</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-comment"><!-- https://mvnrepository.com/artifact/log4j/log4j --></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>log4j<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>log4j<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">version</span>></span>1.2.17<span class="hljs-tag"></<span class="hljs-name">version</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-comment"><!-- https://mvnrepository.com/artifact/com.google.collections/google-collections --></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.google.collections<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>google-collections<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">version</span>></span>1.0-rc2<span class="hljs-tag"></<span class="hljs-name">version</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependencies</span>></span></span></code></pre>
2、工具类:UrlUtil
import java.net.URLEncoder;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
public class UrlUtil {
private <span class="hljs-keyword">static</span> Logger logger = Logger.getLogger(UrlUtil.class);
private final <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> CHARSET_UTF8 = <span class="hljs-string">"utf8"</span>;
<span class="hljs-comment">/**
*
* @param url
* @return
*/</span>
public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> urlEncode(<span class="hljs-built_in">String</span> url) {
<span class="hljs-keyword">if</span> (!StringUtils.isEmpty(url)) {
<span class="hljs-keyword">try</span> {
url = URLEncoder.encode(url, <span class="hljs-string">"UTF-8"</span>);
} <span class="hljs-keyword">catch</span> (Exception e) {
logger.warn(<span class="hljs-string">"Url encode error:"</span> + e.getMessage());
}
}
<span class="hljs-keyword">return</span> url;
}
public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> generateQueryString(<span class="hljs-built_in">Map</span><<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> params, boolean isEncodeKV) {
StringBuilder canonicalizedQueryString = <span class="hljs-keyword">new</span> StringBuilder();
<span class="hljs-keyword">for</span> (<span class="hljs-built_in">Map</span>.Entry<<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> entry : params.entrySet()) {
<span class="hljs-keyword">if</span> (isEncodeKV)
canonicalizedQueryString.append(percentEncode(entry.getKey())).append(<span class="hljs-string">"="</span>)
.append(percentEncode(entry.getValue())).append(<span class="hljs-string">"&"</span>);
<span class="hljs-keyword">else</span>
canonicalizedQueryString.append(entry.getKey()).append(<span class="hljs-string">"="</span>)
.append(entry.getValue()).append(<span class="hljs-string">"&"</span>);
}
<span class="hljs-keyword">if</span> (canonicalizedQueryString.length() > <span class="hljs-number">1</span>) {
canonicalizedQueryString.setLength(canonicalizedQueryString.length() - <span class="hljs-number">1</span>);
}
<span class="hljs-keyword">return</span> canonicalizedQueryString.toString();
}
public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> percentEncode(<span class="hljs-built_in">String</span> value) {
<span class="hljs-keyword">try</span> {
<span class="hljs-comment">// 使用URLEncoder.encode编码后,将"+","*","%7E"做替换即满足 API规定的编码规范</span>
<span class="hljs-keyword">return</span> value == <span class="hljs-literal">null</span> ? <span class="hljs-literal">null</span> : URLEncoder.encode(value, CHARSET_UTF8)
.replace(<span class="hljs-string">"+"</span>, <span class="hljs-string">"%20"</span>).replace(<span class="hljs-string">"*"</span>, <span class="hljs-string">"%2A"</span>).replace(<span class="hljs-string">"%7E"</span>, <span class="hljs-string">"~"</span>);
} <span class="hljs-keyword">catch</span> (Exception e) {
<span class="hljs-comment">//不可能发生的异常</span>
}
<span class="hljs-keyword">return</span> <span class="hljs-string">""</span>;
}
}
3、工具类:SignatureUtils
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Map;
import java.util.TreeMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
public class SignatureUtils {
private final <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> CHARSET_UTF8 = <span class="hljs-string">"utf8"</span>;
private final <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> ALGORITHM = <span class="hljs-string">"UTF-8"</span>;
private final <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> SEPARATOR = <span class="hljs-string">"&"</span>;
public <span class="hljs-keyword">static</span> <span class="hljs-built_in">Map</span><<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> splitQueryString(<span class="hljs-built_in">String</span> url)
throws URISyntaxException, UnsupportedEncodingException {
URI uri = <span class="hljs-keyword">new</span> URI(url);
<span class="hljs-built_in">String</span> query = uri.getQuery();
final <span class="hljs-built_in">String</span>[] pairs = query.split(<span class="hljs-string">"&"</span>);
TreeMap<<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> queryMap = <span class="hljs-keyword">new</span> TreeMap<<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>>();
<span class="hljs-keyword">for</span> (<span class="hljs-built_in">String</span> pair : pairs) {
final int idx = pair.indexOf(<span class="hljs-string">"="</span>);
final <span class="hljs-built_in">String</span> key = idx > <span class="hljs-number">0</span> ? pair.substring(<span class="hljs-number">0</span>, idx) : pair;
<span class="hljs-keyword">if</span> (!queryMap.containsKey(key)) {
queryMap.put(key, URLDecoder.decode(pair.substring(idx + <span class="hljs-number">1</span>), CHARSET_UTF8));
}
}
<span class="hljs-keyword">return</span> queryMap;
}
public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> generate(<span class="hljs-built_in">String</span> method, <span class="hljs-built_in">Map</span><<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> parameter,
<span class="hljs-built_in">String</span> accessKeySecret) throws Exception {
<span class="hljs-built_in">String</span> signString = generateSignString(method, parameter);
System.out.println(<span class="hljs-string">"signString---"</span>+signString);
byte[] signBytes = hmacSHA1Signature(accessKeySecret + <span class="hljs-string">"&"</span>, signString);
<span class="hljs-built_in">String</span> signature = newStringByBase64(signBytes);
System.out.println(<span class="hljs-string">"signature---"</span>+signature);
<span class="hljs-keyword">if</span> (<span class="hljs-string">"POST"</span>.equals(method))
<span class="hljs-keyword">return</span> signature;
<span class="hljs-keyword">return</span> URLEncoder.encode(signature, <span class="hljs-string">"UTF-8"</span>);
}
public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> generateSignString(<span class="hljs-built_in">String</span> httpMethod, <span class="hljs-built_in">Map</span><<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> parameter)
throws IOException {
TreeMap<<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> sortParameter = <span class="hljs-keyword">new</span> TreeMap<<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>>();
sortParameter.putAll(parameter);
<span class="hljs-built_in">String</span> canonicalizedQueryString = UrlUtil.generateQueryString(sortParameter, <span class="hljs-literal">true</span>);
<span class="hljs-keyword">if</span> (<span class="hljs-literal">null</span> == httpMethod) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"httpMethod can not be empty"</span>);
}
StringBuilder stringToSign = <span class="hljs-keyword">new</span> StringBuilder();
stringToSign.append(httpMethod).append(SEPARATOR);
stringToSign.append(percentEncode(<span class="hljs-string">"/"</span>)).append(SEPARATOR);
stringToSign.append(percentEncode(canonicalizedQueryString));
<span class="hljs-keyword">return</span> stringToSign.toString();
}
public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> percentEncode(<span class="hljs-built_in">String</span> value) {
<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">return</span> value == <span class="hljs-literal">null</span> ? <span class="hljs-literal">null</span> : URLEncoder.encode(value, CHARSET_UTF8)
.replace(<span class="hljs-string">"+"</span>, <span class="hljs-string">"%20"</span>).replace(<span class="hljs-string">"*"</span>, <span class="hljs-string">"%2A"</span>).replace(<span class="hljs-string">"%7E"</span>, <span class="hljs-string">"~"</span>);
} <span class="hljs-keyword">catch</span> (Exception e) {
}
<span class="hljs-keyword">return</span> <span class="hljs-string">""</span>;
}
public <span class="hljs-keyword">static</span> byte[] hmacSHA1Signature(<span class="hljs-built_in">String</span> secret, <span class="hljs-built_in">String</span> baseString)
throws Exception {
<span class="hljs-keyword">if</span> (StringUtils.isEmpty(secret)) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IOException(<span class="hljs-string">"secret can not be empty"</span>);
}
<span class="hljs-keyword">if</span> (StringUtils.isEmpty(baseString)) {
<span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}
Mac mac = Mac.getInstance(<span class="hljs-string">"HmacSHA1"</span>);
SecretKeySpec keySpec = <span class="hljs-keyword">new</span> SecretKeySpec(secret.getBytes(CHARSET_UTF8), ALGORITHM);
mac.init(keySpec);
<span class="hljs-keyword">return</span> mac.doFinal(baseString.getBytes(CHARSET_UTF8));
}
public <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> newStringByBase64(byte[] bytes)
throws UnsupportedEncodingException {
<span class="hljs-keyword">if</span> (bytes == <span class="hljs-literal">null</span> || bytes.length == <span class="hljs-number">0</span>) {
<span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">String</span>(Base64.encodeBase64(bytes, <span class="hljs-literal">false</span>), CHARSET_UTF8);
}
public <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> main(<span class="hljs-built_in">String</span>[] args) {
<span class="hljs-built_in">String</span> str = <span class="hljs-string">"GET&%2F&AccessKeyId%3DCd***eHJuMOrT%26Action%3DDescribeInstances%26Format%3DJSON%26RegionId%3Dcn-hangzhou%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D9fdf288**36082ebef%26SignatureVersion%3D1.0%26Timestamp%3D2015-12-21T09%253A05%253A44Z%26Version%3D2014-05-26"</span>;
byte[] signBytes;
<span class="hljs-keyword">try</span> {
signBytes = SignatureUtils.hmacSHA1Signature(<span class="hljs-string">"byc****6HQmH"</span> + <span class="hljs-string">"&"</span>, str.toString());
<span class="hljs-built_in">String</span> signature = SignatureUtils.newStringByBase64(signBytes);
} <span class="hljs-keyword">catch</span> (Exception e) {
<span class="hljs-comment">// TODO Auto-generated catch block</span>
e.printStackTrace();
}
}
}
4、Main方法
import com.google.common.collect.Maps;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import java.net.URI;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.SimpleTimeZone;
import java.util.UUID;
public class Demo1 {
private <span class="hljs-keyword">static</span> <span class="hljs-built_in">String</span> getQueryUrl(<span class="hljs-built_in">Map</span><<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> param, <span class="hljs-built_in">String</span> accessKeyId, <span class="hljs-built_in">String</span> secret) throws Exception {
<span class="hljs-built_in">Date</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();
SimpleDateFormat simpleDateFormat = <span class="hljs-keyword">new</span> SimpleDateFormat(<span class="hljs-string">"yyyy-MM-dd'T'HH:mm:ss'Z'"</span>);
simpleDateFormat.setTimeZone(<span class="hljs-keyword">new</span> SimpleTimeZone(<span class="hljs-number">0</span>, <span class="hljs-string">"GMT"</span>));
<span class="hljs-built_in">String</span> timestamp = simpleDateFormat.format(date);
<span class="hljs-built_in">Map</span><<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> publicParam = Maps.newHashMap();
publicParam.put(<span class="hljs-string">"AccessKeyId"</span>, accessKeyId);
publicParam.put(<span class="hljs-string">"Format"</span>, <span class="hljs-string">"json"</span>);
publicParam.put(<span class="hljs-string">"SignatureMethod"</span>, <span class="hljs-string">"Hmac-SHA1"</span>);
publicParam.put(<span class="hljs-string">"SignatureNonce"</span>, UUID.randomUUID().toString());
publicParam.put(<span class="hljs-string">"SignatureVersion"</span>, <span class="hljs-string">"1.0"</span>);
publicParam.put(<span class="hljs-string">"Timestamp"</span>, timestamp);
publicParam.put(<span class="hljs-string">"Version"</span>, <span class="hljs-string">"2018-12-03"</span>);
publicParam.put(<span class="hljs-string">"RegionId"</span>, <span class="hljs-string">"cn-shanghai"</span>);
<span class="hljs-keyword">if</span> (param != <span class="hljs-literal">null</span>) {
<span class="hljs-keyword">for</span> (<span class="hljs-built_in">Map</span>.Entry<<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> entry : param.entrySet()) {
publicParam.put(entry.getKey(), entry.getValue());
}
}
<span class="hljs-built_in">String</span> s = SignatureUtils.generateSignString(<span class="hljs-string">"POST"</span>, publicParam);
byte[] signBytes;
signBytes = SignatureUtils.hmacSHA1Signature(secret + <span class="hljs-string">"&"</span>, s);
<span class="hljs-built_in">String</span> signature = SignatureUtils.newStringByBase64(signBytes);
publicParam.put(<span class="hljs-string">"Signature"</span>, signature);
<span class="hljs-built_in">String</span> url = <span class="hljs-string">"http://face.cn-shanghai.aliyuncs.com/?"</span>;
<span class="hljs-comment">//对参数进行url编码</span>
<span class="hljs-keyword">for</span> (<span class="hljs-built_in">Map</span>.Entry<<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> entry : publicParam.entrySet()) {
publicParam.put(entry.getKey(), URLEncoder.encode(entry.getValue().toString(), <span class="hljs-string">"UTF-8"</span>));
}
<span class="hljs-comment">//拼接请求url</span>
<span class="hljs-keyword">for</span> (<span class="hljs-built_in">Map</span>.Entry<<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> entry : publicParam.entrySet()) {
url = url + entry + <span class="hljs-string">"&"</span>;
}
<span class="hljs-comment">//去掉最后一个&</span>
url = url.substring(<span class="hljs-number">0</span>, url.length() - <span class="hljs-number">1</span>);
<span class="hljs-keyword">return</span> url;
}
public <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> main(<span class="hljs-built_in">String</span>[] args) {
<span class="hljs-keyword">try</span> {
<span class="hljs-built_in">Map</span><<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>> map = Maps.newHashMap();
map.put(<span class="hljs-string">"Action"</span>, <span class="hljs-string">"DetectFace"</span>);
<span class="hljs-built_in">String</span> imageUrl_1 = <span class="hljs-string">"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559655604341&di=3d6995f6dee1c4795d1827e754a00452&imgtype=0&src=http%3A%2F%2Fimg0.ph.126.net%2F90u9atgu46nnziAm1NMAGw%3D%3D%2F6631853916514183512.jpg"</span>;
map.put(<span class="hljs-string">"ImageUrl"</span>,imageUrl_1);
<span class="hljs-comment">// access_key_id, access_key_secret 获取参考链接:https://yq.aliyun.com/articles/693979?spm=a2c4e.11155435.0.0.319326a2bKJ90g</span>
<span class="hljs-built_in">String</span> accessKeyId = <span class="hljs-string">"*******"</span>;
<span class="hljs-built_in">String</span> secret = <span class="hljs-string">"*******"</span>;
<span class="hljs-built_in">String</span> url = getQueryUrl(map, accessKeyId, secret);
<span class="hljs-comment">// 使用生成的 URL 创建POST请求</span>
URIBuilder builder = <span class="hljs-keyword">new</span> URIBuilder(url);
URI uri = builder.build();
HttpPost request = <span class="hljs-keyword">new</span> HttpPost(uri);
HttpClient httpclient = HttpClients.createDefault();
HttpResponse response = httpclient.execute(request);
HttpEntity entity = response.getEntity();
<span class="hljs-keyword">if</span> (entity != <span class="hljs-literal">null</span>)
{
System.out.println(EntityUtils.toString(entity));
}
}
<span class="hljs-keyword">catch</span> (Exception e)
{
System.out.println(e.getMessage());
}
}
}
5、运行结果
参考链接
参考链接