Anda harus menandatangani semua permintaan API untuk memastikan keamanan data. Alibaba Cloud menggunakan tanda tangan permintaan untuk memverifikasi identitas pemanggil API. Oleh karena itu, setiap permintaan API harus mencakup informasi tanda tangan, baik dikirim melalui HTTP maupun HTTPS.
Menandatangani permintaan
Untuk menandatangani permintaan API, Anda harus masuk ke Konsol Manajemen Pengguna untuk mendapatkan pasangan AccessKey dan menghitung kode autentikasi pesan (MAC). Pasangan AccessKey terdiri dari ID AccessKey dan Rahasia AccessKey. ID AccessKey digunakan untuk memverifikasi identitas pemanggil API, sedangkan Rahasia AccessKey digunakan untuk mengenkripsi dan memverifikasi string tanda tangan. Pastikan kerahasiaan Rahasia AccessKey dengan ketat.
IoT Platform menyediakan SDK untuk berbagai bahasa pemrograman seperti Java, Python, dan PHP. Jika Anda menggunakan SDK untuk memanggil operasi API, Anda dapat melewati proses penandatanganan. Untuk informasi lebih lanjut tentang cara menggunakan SDK, lihat IoT Platform SDK (Asli) dan IoT Platform SDK (Ditingkatkan).
Untuk menandatangani permintaan, ikuti langkah-langkah berikut:
Buat string kueri kanonis dengan mengatur parameter permintaan.
Urutkan parameter permintaan.
Urutkan semua parameter permintaan secara alfabetis. Parameter ini mencakup semua parameter umum dan spesifik operasi, kecuali parameter Signature.
nullJika Anda menggunakan metode GET untuk mengirim permintaan, parameter yang mengikuti tanda tanya (
?) dan dihubungkan oleh ampersand (&) dalam URL permintaan adalah parameter permintaan.Enkode string kueri kanonis.
Gunakan UTF-8 untuk mengenkode nama dan nilai parameter permintaan sesuai dengan RFC 3986. Format enkode harus mematuhi aturan berikut:
Huruf, angka, tanda hubung (
-), garis bawah (_), titik (.), dan tilde (~) tidak perlu dienkode.Karakter lainnya harus dienkode persen dalam format
%XY.XYmewakili kode ASCII karakter dalam notasi heksadesimal. Misalnya, tanda kutip ganda (") dienkode sebagai%22.Karakter UTF-8 diperpanjang dienkode dalam format
%XY%ZA….Spasi harus dienkode sebagai
%20. Jangan enkode spasi sebagai tanda tambah (+).
Metode enkode ini mirip tetapi sedikit berbeda dari algoritma enkode Multipurpose Internet Mail Extensions (MIME)
application/x-www-form-urlencoded.Jika Anda menggunakan
java.net.URLEncoderdari pustaka standar Java, gunakanpercentEncodeuntuk mengenkode parameter permintaan dan nilainya. Dalam string kueri yang dienkode, ganti tanda tambah (+) dengan%20, tanda bintang (*) dengan%2A, dan%7Edengan tilde (~). Dengan cara ini, Anda dapat memperoleh string kueri yang sesuai dengan aturan enkode sebelumnya.private static final String ENCODING = "UTF-8"; private static String percentEncode(String value) throws UnsupportedEncodingException { return value != null ? URLEncoder.encode(value, ENCODING).replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null; }Hubungkan nama dan nilai parameter permintaan yang dienkode menggunakan tanda sama dengan (=).
Urutkan parameter permintaan yang dienkode berdasarkan nama parameter secara alfabetis, dan gunakan ampersand (
&) untuk menghubungkan parameter.
Dengan cara ini, string kueri kanonis (CanonicalizedQueryString) dibuat.
Buat string-to-sign.
Buat string-to-sign dari string kueri kanonis yang dienkode menggunakan metode
percentEncode. Contoh kode berikut menunjukkan cara membuat string-to-sign:StringToSign= HTTPMethod + "&" + percentEncode("/") + "&" + percentEncode(CanonicalizedQueryString)Parameter:
HTTPMethod: metode HTTP yang digunakan untuk mengirim permintaan, seperti GET.
percentEncode(“/“): gunakan UTF-8 untuk mengenkode garis miring (/) sebagai %2F.
percentEncode(CanonicalizedQueryString) // Enkode string kueri kanonis yang dibuat pada langkah sebelumnya.
Hitung kode autentikasi pesan berbasis hash (HMAC) dari string-to-sign.
Hitung HMAC dari
string-to-signberdasarkan RFC 2104.HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) )nullGunakan algoritma Secure Hash Algorithm 1 (SHA-1) untuk menghitung HMAC dari string-to-sign. Rahasia AccessKey yang ditambahkan ampersand (
&) (ASCII:38) digunakan sebagai kunci untuk perhitungan HMAC.Hitung string tanda tangan.
Enkode HMAC dalam Base64 untuk mendapatkan string tanda tangan.
Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) )Tambahkan string tanda tangan.
Tambahkan string tanda tangan ke permintaan sebagai parameter Signature dan lakukan enkode URL berdasarkan RFC 3986.
Contoh
Contoh berikut menunjukkan cara menandatangani permintaan. Dalam contoh ini, operasi Pub dari IoT Platform dipanggil. Parameter permintaan berikut dikonfigurasikan dalam URL permintaan: AccessKeyId=testid, AccessKeySecret=testsecret, ProductKey=12345abcde, TopicFullName=/12345abcde/testdevice/user/get, MessageContent=aGVsbG8gd29ybGQ, dan Qos=0.
Buat dan enkode string kueri kanonis.
http://iot.cn-shanghai.aliyuncs.com/?Action=Pub&MessageContent=aGVsbG8gd29ybGQ&Timestamp=2018-07-31T07:43:57Z&SignatureVersion=1.0&Format=XML&Qos=0&SignatureNonce=3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf&Version=2018-01-20&AccessKeyId=testid&SignatureMethod=HMAC-SHA1&RegionId=cn-shanghai&ProductKey=12345abcde&TopicFullName=/12345abcde/testdevice/user/getBuat string-to-sign dari string kueri kanonis yang dienkode.
GET&%2F&AccessKeyId%3Dtestid%26Action%3DPub%26Format%3DXML%26MessageContent%3DaGVsbG8gd29ybGQ%26ProductKey%3D12345abcde%26Qos%3D0%26RegionId%3Dcn-shanghai%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf%26SignatureVersion%3D1.0%26Timestamp%3D2018-07-31T07%253A43%253A57Z%26TopicFullName%3D%252F12345abcde%252Ftestdevice%252Fuser%252Fget%26Version%3D2018-01-20Hitung string tanda tangan.
Dalam contoh ini, Rahasia AccessKey adalah
testsecret. Kunci yang digunakan untuk perhitungan HMAC adalah testsecret&. Kode berikut menunjukkan string tanda tangan yang dihitung:NUh3otvAoXOZmG/a2gDShh6Ze9w=Tambahkan string tanda tangan ke permintaan sebagai parameter Signature. Kode berikut menunjukkan URL permintaan yang ditandatangani:
http://iot.cn-shanghai.aliyuncs.com/?MessageContent=aGVsbG8gd29ybGQ&Action=Pub&Timestamp=2018-07-31T07%253A43%253A57Z&SignatureVersion=1.0&Format=XML&Qos=0&SignatureNonce=3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf&Version=2018-01-20&AccessKeyId=testid&Signature=NUh3otvAoXOZmG%2Fa2gDShh6Ze9w%3D&SignatureMethod=HMAC-SHA1&RegionId=cn-shanghai&ProductKey=12345abcde&TopicFullName=%2F12345abcde%2Ftestdevice%2Fuser%2Fget
Contoh kode dalam Java
Contoh berikut menunjukkan cara mendapatkan tanda tangan menggunakan Java:
Tambahkan dependensi berikut ke proyek Maven Anda.
<!-- Apache Commons Lang 3.x --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> <!-- Gunakan versi terbaru --> </dependency> <!-- Apache Commons Codec --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-codec</artifactId> <version>1.15</version> <!-- Gunakan versi terbaru --> </dependency>Ubah file Config.java.
/* * Copyright © 2018 Alibaba. All rights reserved. */ package com.aliyun.iot.demo.sign; /** * Konfigurasi tanda tangan untuk API server. * * @author: ali * @version: 0.1 2018-08-08 08:23:54 */ public class Config { // Pasangan AccessKey Anda. public static String accessKey = "123456******3456"; public static String accessKeySecret = "123456******34567******4567890"; public final static String CHARSET_UTF8 = "utf8"; }Parameter
Contoh
Deskripsi
accessKey
123456******3456
Masuk ke konsol IoT Platform, arahkan pointer ke foto profil, lalu klik AccessKey Management untuk mendapatkan ID AccessKey dan Rahasia AccessKey.
nullJika Anda menggunakan pengguna RAM, Anda harus melampirkan kebijakan izin AliyunIOTFullAccess ke pengguna tersebut. Kebijakan ini memungkinkan pengguna untuk mengelola sumber daya IoT Platform. Jika tidak, koneksi dengan IoT Platform gagal. Untuk informasi lebih lanjut tentang cara memberikan izin kepada pengguna RAM, lihat Akses IoT Platform sebagai pengguna RAM.
accessKeySecret
123456******34567******4567890
Ubah file UrlUtil.java.
/* * Copyright © 2018 Alibaba. All rights reserved. */ package com.aliyun.iot.demo.sign; import java.net.URLEncoder; import java.util.Map; import org.apache.commons.lang3.StringUtils; /** * Kelas pemrosesan URL. * * @author: ali * @version: 0.1 2018-06-21 20:40:52 */ public class UrlUtil { private final static String CHARSET_UTF8 = "utf8"; public static String urlEncode(String url) { if (!StringUtils.isEmpty(url)) { try { url = URLEncoder.encode(url, "UTF-8"); } catch (Exception e) { System.out.println("Url encode error:" + e.getMessage()); } } return url; } public static String generateQueryString(Map<String, String> params, boolean isEncodeKV) { StringBuilder canonicalizedQueryString = new StringBuilder(); for (Map.Entry<String, String> entry : params.entrySet()) { if (isEncodeKV) canonicalizedQueryString.append(percentEncode(entry.getKey())).append("=") .append(percentEncode(entry.getValue())).append("&"); else canonicalizedQueryString.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); } if (canonicalizedQueryString.length() > 1) { canonicalizedQueryString.setLength(canonicalizedQueryString.length() - 1); } return canonicalizedQueryString.toString(); } public static String percentEncode(String value) { try { // Setelah Anda menggunakan URLEncoder.encode untuk enkode, ganti tanda tambah (+), tanda bintang (*), dan %7E dengan nilai yang sesuai dengan standar enkode API. return value == null ? null : URLEncoder.encode(value, CHARSET_UTF8).replace("+", "%20").replace("*", "%2A").replace("%7E", "~"); } catch (Exception e) { } return ""; } }Ubah file SignatureUtils.java.
/* * Copyright © 2018 Alibaba. All rights reserved. */ package com.aliyun.iot.demo.sign; 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.lang3.StringUtils; /** * Konfigurasi tanda tangan untuk API server. * * @author: ali * @version: 0.1 2018-06-21 20:47:05 */ public class SignatureUtils { private final static String CHARSET_UTF8 = "utf8"; private final static String ALGORITHM = "HmacSHA1"; private final static String SEPARATOR = "&"; public static Map<String, String> splitQueryString(String url) throws URISyntaxException, UnsupportedEncodingException { URI uri = new URI(url); String query = uri.getQuery(); final String[] pairs = query.split("&"); TreeMap<String, String> queryMap = new TreeMap<String, String>(); for (String pair : pairs) { final int idx = pair.indexOf("="); final String key = idx > 0 ? pair.substring(0, idx) : pair; if (!queryMap.containsKey(key)) { queryMap.put(key, URLDecoder.decode(pair.substring(idx + 1), CHARSET_UTF8)); } } return queryMap; } public static String generate(String method, Map<String, String> parameter, String accessKeySecret) throws Exception { String signString = generateSignString(method, parameter); System.out.println("signString---" + signString); byte[] signBytes = hmacSHA1Signature(accessKeySecret + "&", signString); String signature = newStringByBase64(signBytes); System.out.println("signature----" + signature); if ("POST".equals(method)) return signature; return URLEncoder.encode(signature, "UTF-8"); } public static String generateSignString(String httpMethod, Map<String, String> parameter) throws IOException { TreeMap<String, String> sortParameter = new TreeMap<String, String>(); sortParameter.putAll(parameter); String canonicalizedQueryString = UrlUtil.generateQueryString(sortParameter, true); if (null == httpMethod) { throw new RuntimeException("httpMethod can not be empty"); } StringBuilder stringToSign = new StringBuilder(); stringToSign.append(httpMethod).append(SEPARATOR); stringToSign.append(percentEncode("/")).append(SEPARATOR); stringToSign.append(percentEncode(canonicalizedQueryString)); return stringToSign.toString(); } public static String percentEncode(String value) { try { return value == null ? null : URLEncoder.encode(value, CHARSET_UTF8).replace("+", "%20").replace("*", "%2A").replace("%7E", "~"); } catch (Exception e) { } return ""; } public static byte[] hmacSHA1Signature(String secret, String baseString) throws Exception { if (StringUtils.isEmpty(secret)) { throw new IOException("secret can not be empty"); } if (StringUtils.isEmpty(baseString)) { return null; } Mac mac = Mac.getInstance("HmacSHA1"); SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(CHARSET_UTF8), ALGORITHM); mac.init(keySpec); return mac.doFinal(baseString.getBytes(CHARSET_UTF8)); } public static String newStringByBase64(byte[] bytes) throws UnsupportedEncodingException { if (bytes == null || bytes.length == 0) { return null; } return new String(Base64.encodeBase64(bytes, false), CHARSET_UTF8); } }Ubah file Main.java.
/* * Copyright © 2018 Alibaba. All rights reserved. */ package com.aliyun.iot.demo.sign; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; /** * Entri utama ke alat tanda tangan. * * @author: ali * @version: 0.1 2018-09-18 15:06:48 */ public class Main { // 1. Ubah pasangan AccessKey dalam file Config.java. // 2. Anda harus menentukan semua parameter. Kami sarankan Anda menggunakan Metode 2 dalam deskripsi berikut. // 3. Nilai "Final signature" adalah tanda tangan yang ingin Anda peroleh. public static void main(String[] args) throws UnsupportedEncodingException { // Metode 1 System.out.println("Metode 1:"); String str = "GET&%2F&AccessKeyId%3D" + Config.accessKey + "%26Action%3DRegisterDevice%26DeviceName%3D1533023037%26Format%3DJSON%26ProductKey%3DaxxxUtgaRLB%26RegionId%3Dcn-shanghai%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D1533023037%26SignatureVersion%3D1.0%26Timestamp%3D2018-07-31T07%253A43%253A57Z%26Version%3D2018-01-20"; byte[] signBytes; try { signBytes = SignatureUtils.hmacSHA1Signature(Config.accessKeySecret + "&", str.toString()); String signature = SignatureUtils.newStringByBase64(signBytes); System.out.println("signString---" + str); System.out.println("signature----" + signature); System.out.println("Final signature:" + URLEncoder.encode(signature, Config.CHARSET_UTF8)); } catch (Exception e) { e.printStackTrace(); } System.out.println(); // Metode 2 System.out.println("Metode 2:"); Map<String, String> map = new HashMap<String, String>(); // Parameter umum. map.put("Format", "JSON"); map.put("Version", "2018-01-20"); map.put("AccessKeyId", Config.accessKey); map.put("SignatureMethod", "HMAC-SHA1"); map.put("Timestamp", "2018-07-31T07:43:57Z"); map.put("SignatureVersion", "1.0"); map.put("SignatureNonce", "1533023037"); map.put("RegionId", "cn-shanghai"); // Parameter permintaan. map.put("Action", "RegisterDevice"); map.put("DeviceName", "1533023037"); map.put("ProductKey", "axxxUtgaRLB"); try { String signature = SignatureUtils.generate("GET", map, Config.accessKeySecret); System.out.println("Final signature:" + signature); } catch (Exception e) { e.printStackTrace(); } System.out.println(); } }