前言:訪問s3對象存儲可以通過官方的sdk和使用restful的方式來訪問。推薦使用sdk的方式,因為sdk不需要計算簽名並且有完整的錯誤機制。下面是使用restful的方式來進行訪問
參考aws api資料 https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/API/API_Operations_Amazon_Simple_Storage_Service.html
1、使用方式為V2請求鑒權,請求的鑒權是指通過HTTP和HTTPS消息頭Authorizon 進⾏鑒權,標頭格式如下
Authorization: AWS AWSAccessKeyId:Signature
- 語法格式:
Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
StringToSign = HTTP-Verb + "\n" + Content-MD5 + "\n" + Content-Type + "\n" + Date + "\n" + CanonicalizedAmzHeaders + CanonicalizedResource;
Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ) );
- 簽名構造參數說明:
1)HTTP-Verb 描述:
指定接⼝操作的⽅法。即為 “PUT\GET\DELETE” 等字符串。
2)Date 描述:
⽣成請求的時間,該時間格式遵循 RFC 1123。 Content-Type 描述:內容類型,⽤於指定消息類型。
3)Content-MD5 描述:
按照 RFC 1864 標准計算出消息體的 MD5 摘要字符串。
4)CanonicalizedAmzHeaders 描述:
以 “x-amz-” 作為前綴的消息頭,如:“x-amz-data,x-amz-acl”。 1. ⾃定義字段中的所有字符要轉為⼩寫,需要添加多個字段時,要將所有字段按照字典序進 ⾏排序。 2. 在添加⾃定義字段時,如果有重名的字段,則需要進⾏合並。如:x-amz-meta-name: name1 和x-amz-meta-name:name2,需要合並成x-amz-meta-name:name1,name2。 3. 當⾃定義字段中,含有⾮ ASCII 碼或不可識別字符時,需要進⾏ base64 編碼。 4. 當⾃定義字段中含有⽆意義空格或 table 鍵時,需要摒棄。例:x-amz-meta-name: name(name前 帶有⼀個⽆意義的空格),需要轉換為:x-amz-meta-name:name 5. 每⼀個⾃定義字段最后都需要另起新⾏。 公式:CanonicalizedResource = [ "/" + Bucket ] + <http-request-uri, from="" the="" protocol="" name="" up="" to="" query="" string=""> + [ subresource, if present. For example "?acl"]; Authorization: AWS AWSAccessKeyId:Signature 語法格式: Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature; Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ) ); StringToSign = HTTP-Verb + "\n" + Content-MD5 + "\n" + Content-Type + "\n" + Date + "\n" + CanonicalizedAmzHeaders + CanonicalizedResource;
5)CanonicalizedResource 描述:
表示HTTP請求所指定的存儲資源,構造⽅式如下:<桶名+對象名>+[⼦資源]+[查詢字符 串] 1. 桶名和對象名 2. 如果有⼦資源,則將⼦資源添加進來; 3. 如有查詢字符串那么將這些查詢字符串及其請求值按照字典序從⼩到⼤排列。 公式:CanonicalizedAmzHeaders = <下⾯描述>
- 簡單實例:
對象GET
1、請求消息頭
GET /johnsmith/photos/puppy.jpg HTTP/1.1
Host: s3.xsky.com
Date: Tue, 27 Mar 2007 19:36:42 +0000
Authorization: AWS AKIAIOSFODNN7EXAMPLE: bWq2s1WEIj+Ydj0vQ697zp+IXMU=
2、StringToSign
GET\n
\n
\n
Tue, 27 Mar 2007 19:36:42 +0000\n
/johnsmith/photos/puppy.jpg
下面是java代碼實現簽名的過程並獲取桶內對象
package restful;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class create_bucket {
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, IOException {
//http建立連接時候先發tcp連接請求,http 1.0協議每次發送http請求都需要建立一個新的TCP連接。http1.1允許一個tcp連接反復發送和響應。http2.0允許同時發送多個http請求。不需要按序返回
//GET請求的參數必須附加在URL上,因為URL的長度限制,GET請求的參數不能太多,而POST請求的參數就沒有長度限制,因為POST請求的參數必須放到Body中。
//生成RFC1123 格式的時間
SimpleDateFormat sdf3 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z",Locale.US);
sdf3.setTimeZone(TimeZone.getTimeZone("GMT"));
String rfc1123_3 = sdf3.format(new Date());
//簽名構造參數 變為UTF-8格式
String stringToSign ="PUT\n\n\n"+rfc1123_3+"\n/aaaaab1";
byte[] utf8Tosign = stringToSign.getBytes("UTF-8");
String utf8str = new String(utf8Tosign,"UTF-8");
//計算簽名
String s3Key="BXM4ATD5KZ3EJLP8IEM0";
byte[] s3Secret="NfkfMSzAv8XETXmpsj8XDrAjB3fIuJnkxE3NzbGF".getBytes();
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(s3Secret, "HmacSHA1"));
mac.update(utf8str.getBytes());
byte[] result = mac.doFinal();
String signature = Base64.getEncoder().encodeToString(result);
System.out.println("簽名為: "+signature);
URL url = new URL("http://10.255.20.180:8060/aaaaab1");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("PUT");
conn.setUseCaches(false);
// conn.setConnectTimeout(30000); //30秒連接超時
// conn.setReadTimeout(30000); //30秒讀取超時
conn.setDoOutput(true); // 設置是否向httpUrlConnection輸出,默認情況下是false
conn.setDoInput(true); //設置是否從httpUrlConnection讀入,默認情況下是true;
conn.setRequestProperty("Date", rfc1123_3);
conn.setRequestProperty("Authorization", "AWS" + " " + s3Key + ":" + signature);
conn.connect();
System.out.println(conn.getResponseCode());
Map<String, List<String>> map = conn.getHeaderFields();
for (String key:map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
InputStream input = conn.getInputStream();
BufferedReader buff = new BufferedReader(new InputStreamReader(input));
String line = null;
while((line = buff.readLine()) != null){
line = new String(line.getBytes("UTF-8"));
System.out.println(line);
}
input.close(); //關閉流,完成后要及時關閉避免占用資源
buff.close();
conn.disconnect();
}
}
shell方式創建bucket
