前言:访问s3对象存储可以通过官方的sdk和使用restful的方式来访问。推荐使用sdk的方式,因为sdk不需要计算签名并且有完整的错误机制。下面是使用restful的方式来进行访问


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:Signatur

e 语法格式:

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

创建名为"mygod"的bucket。

#!/bin/bash
bucket="mygod"
dateValue=`date -R -u`
resource="/${bucket}"
stringToSign="PUT\n\n\n${dateValue}\n${resource}"
s3Key="BXM4ATD5KZ3EJLP8IEM0"
s3Secret="NfkfMSzAv8XETXmpsj8XDrAjB3fIuJnkxE3NzbGF"
signature=`echo -en ${stringToSign} | openssl sha1 -hmac ${s3Secret} -binary | base64`
curl -v -X PUT "http://10.255.20.180:8060/${bucket}" \
        -H "Date: ${dateValue}" \
        -H "Authorization: AWS ${s3Key}:${signature}"