1.3. 관리 인증 요청

Amazon의 S3 서비스는 액세스 키와 요청 헤더의 해시와 시크릿 키를 사용하여 요청을 인증합니다. 인증된 요청, 특히 대규모 업로드를 SSL 오버헤드 없이 제공하는 이점이 있습니다.

S3 API의 대부분의 사용 사례는 Amazon SDK for Java 또는 Python Boto에서 AmazonS3Client 와 같은 오픈 소스 S3 클라이언트를 사용하는 것입니다. 이러한 라이브러리는 Ceph Object Gateway Admin API를 지원하지 않습니다. 이러한 라이브러리를 하위 클래스로 확장하여 Ceph Admin API를 지원할 수 있습니다. 또는 고유한 게이트웨이 클라이언트를 만들 수 있습니다.

execute() 메서드 생성

이 섹션의 CephAdminAPI 예제 클래스에서는 요청 매개 변수를 사용하고, 요청을 인증하고, Ceph Admin API를 호출하고, 응답을 수신할 수 있는 execute() 메서드를 생성하는 방법을 보여줍니다.

CephAdminAPI 클래스 예제에서는 지원되지 않거나 상업적 용도로 사용할 수 없습니다. 이는 예시적인 목적으로만 사용됩니다.

Ceph 개체 게이트웨이 호출

클라이언트 코드에 는 CRUD 작업을 시연하기 위해 Ceph Object Gateway에 대한 5개의 호출이 포함되어 있습니다.

  • 사용자 만들기
  • 사용자 가져오기
  • 사용자 수정
  • 하위 사용자 만들기
  • 사용자 삭제

이 예제를 사용하려면 httpcomponents-client-4.5.3 Apache HTTP 구성 요소를 가져옵니다. 예를 들어 여기에서 다운로드할 수 있습니다. http://hc.apache.org/downloads.cgi. 그런 다음 tar 파일의 압축을 풀고 lib 디렉토리로 이동하여 JAVA_HOME 디렉터리의 /jre/lib/ext 디렉터리 또는 사용자 지정 classpath에 콘텐츠를 복사합니다.

CephAdminAPI 클래스 예제를 검사할 때 execute() 메서드는 HTTP 메서드, 요청 경로, 선택적 하위 리소스, 지정되지 않은 경우 null, 매개변수 맵을 사용합니다. 하위 리소스(예: subuser, key )로 실행하려면 하위 리소스를 execute() 메서드에서 인수로 지정해야 합니다.

예제 메서드:

  1. URI를 빌드합니다.
  2. HTTP 헤더 문자열을 작성합니다.
  3. HTTP 요청을 인스턴스화합니다(예: PUT,POST,GET,DELETE ).
  4. HTTP 헤더 문자열 및 요청 헤더에 Date 헤더를 추가합니다.
  5. Authorization 헤더를 HTTP 요청 헤더에 추가합니다.
  6. HTTP 클라이언트를 인스턴스화하고 인스턴스화한 HTTP 요청을 전달합니다.
  7. 요청을 합니다.
  8. 응답을 반환합니다.

헤더 문자열 빌드

헤더 문자열을 빌드하는 것은 Amazon의 S3 인증 프로시저를 포함하는 프로세스의 일부입니다. 특히 예제 메서드는 다음을 수행합니다.

  1. 요청 유형(예: PUT,POST,GET,DELETE )을 추가합니다.
  2. 날짜를 추가합니다.
  3. requestPath를 추가합니다.

요청 유형은 선행 또는 후행 공백이 없는 대문자이어야 합니다. 공백을 비우지 않으면 인증이 실패합니다. 날짜는 kafka로 표시되거나 인증이 실패합니다.

예시적인 방법에는 다른 헤더가 없습니다. Amazon S3 인증 절차에서는 사전 단위로 x-amz 헤더를 정렬합니다. x-amz 헤더를 추가하는 경우 사전순으로 추가해야 합니다.

헤더 문자열을 빌드했으면 다음 단계는 HTTP 요청을 인스턴스화하고 URI를 전달하는 것입니다. 시험 분할 방법은 PUT 을 사용하여 사용자 및 하위 사용자 생성, 사용자 변경을 위한 POST, 사용자 삭제용 DELETE 를 사용합니다.

요청을 인스턴스화하면 Date 헤더와 Authorization 헤더를 추가합니다. Amazon의 S3 인증은 표준 Authorization 헤더를 사용하며 다음과 같은 구조를 갖습니다.

Authorization: AWS ACCESS_KEY:HASH_OF_HEADER_AND_SECRET

CephAdminAPI 예제 클래스에는 base64Sha1Hmac() 메서드가 있습니다. 이 방법은 헤더 문자열과 admin 사용자의 시크릿 키를 사용하고 SHA1 HMAC를 base-64 인코딩 문자열로 반환합니다. 각 execute() 호출은 동일한 코드 줄을 호출하여 Authorization 헤더를 빌드합니다.

httpRequest.addHeader("Authorization", "AWS " + this.getAccessKey() + ":" + base64Sha1Hmac(headerString.toString(), this.getSecretKey()));

다음 CephAdminAPI 예제 클래스를 사용하려면 access 키, secret 키 및 끝점을 생성자에 전달해야 합니다. 클래스는 런타임 시 변경할 수 있는 접근자 메서드를 제공합니다.

예제

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.ZoneId;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.Header;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.http.client.utils.URIBuilder;

import java.util.Base64;
import java.util.Base64.Encoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.Mac;

import java.util.Map;
import java.util.Iterator;
import java.util.Set;
import java.util.Map.Entry;

public class CephAdminAPI {

	/*
	 * Each call must specify an access key, secret key, endpoint and format.
	 */
	String accessKey;
	String secretKey;
	String endpoint;
	String scheme = "http"; //http only.
	int port = 80;

	/*
	 * A constructor that takes an access key, secret key, endpoint and format.
	 */
	public CephAdminAPI(String accessKey, String secretKey, String endpoint){
		this.accessKey = accessKey;
		this.secretKey = secretKey;
		this.endpoint = endpoint;
	}

	/*
	 * Accessor methods for access key, secret key, endpoint and format.
	 */
	public String getEndpoint(){
		return this.endpoint;
	}

	public void setEndpoint(String endpoint){
		this.endpoint = endpoint;
	}

	public String getAccessKey(){
		return this.accessKey;
	}

	public void setAccessKey(String accessKey){
		this.accessKey = accessKey;
	}

	public String getSecretKey(){
		return this.secretKey;
	}

	public void setSecretKey(String secretKey){
		this.secretKey = secretKey;
	}

	/*
	 * Takes an HTTP Method, a resource and a map of arguments and
	 * returns a CloseableHTTPResponse.
	 */
	public CloseableHttpResponse execute(String HTTPMethod, String resource,
                                        String subresource, Map arguments) {

		String httpMethod = HTTPMethod;
		String requestPath = resource;
		StringBuffer request = new StringBuffer();
		StringBuffer headerString = new StringBuffer();
		HttpRequestBase httpRequest;
		CloseableHttpClient httpclient;
		URI uri;
		CloseableHttpResponse httpResponse = null;

		try {

			uri = new URIBuilder()
				.setScheme(this.scheme)
				.setHost(this.getEndpoint())
				.setPath(requestPath)
				.setPort(this.port)
				.build();


			if (subresource != null){
				uri = new URIBuilder(uri)
					.setCustomQuery(subresource)
					.build();
			}


			for (Iterator iter = arguments.entrySet().iterator();
			iter.hasNext();) {
				Entry entry = (Entry)iter.next();
				uri = new URIBuilder(uri)
					.setParameter(entry.getKey().toString(),
                                 entry.getValue().toString())
					.build();

			}

			request.append(uri);

			headerString.append(HTTPMethod.toUpperCase().trim() + "\n\n\n");

			OffsetDateTime dateTime = OffsetDateTime.now(ZoneId.of("GMT"));
			DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME;
			String date = dateTime.format(formatter);

			headerString.append(date + "\n");
			headerString.append(requestPath);

			if (HTTPMethod.equalsIgnoreCase("PUT")){
				httpRequest = new HttpPut(uri);
			} else if (HTTPMethod.equalsIgnoreCase("POST")){
				httpRequest = new HttpPost(uri);
			} else if (HTTPMethod.equalsIgnoreCase("GET")){
				httpRequest = new HttpGet(uri);
			} else if (HTTPMethod.equalsIgnoreCase("DELETE")){
				httpRequest = new HttpDelete(uri);
			} else {
				System.err.println("The HTTP Method must be PUT,
				POST, GET or DELETE.");
				throw new IOException();
			}

			httpRequest.addHeader("Date", date);
			httpRequest.addHeader("Authorization", "AWS " + this.getAccessKey()
			+ ":" + base64Sha1Hmac(headerString.toString(),
			this.getSecretKey()));

			httpclient = HttpClients.createDefault();
			httpResponse = httpclient.execute(httpRequest);

		} 	catch  (URISyntaxException e){
			System.err.println("The URI is not formatted properly.");
			e.printStackTrace();
		}  catch (IOException e){
			System.err.println("There was an error making the request.");
			e.printStackTrace();
		}
			return httpResponse;
	}

	/*
	 * Takes a uri and a secret key and returns a base64-encoded
	 * SHA-1 HMAC.
	 */
	public String base64Sha1Hmac(String uri, String secretKey) {
		try {

			byte[] keyBytes = secretKey.getBytes("UTF-8");
			SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");

			Mac mac = Mac.getInstance("HmacSHA1");
			mac.init(signingKey);

			byte[] rawHmac = mac.doFinal(uri.getBytes("UTF-8"));

			Encoder base64 = Base64.getEncoder();
			return base64.encodeToString(rawHmac);

		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

}

후속 CephAdminAPIClient 예제에서는 CephAdminAPI 클래스를 인스턴스화하고, 요청 매개 변수에 대한 맵을 빌드하며, execute() 메서드를 사용하여 사용자를 생성, 가져오기, 업데이트 및 삭제하는 방법을 보여줍니다.

예제

import java.io.IOException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.HttpEntity;
import org.apache.http.util.EntityUtils;
import java.util.*;


public class CephAdminAPIClient {

	public static void main (String[] args){

		CephAdminAPI adminApi = new CephAdminAPI ("FFC6ZQ6EMIF64194158N",
		                            "Xac39eCAhlTGcCAUreuwe1ZuH5oVQFa51lbEMVoT",
		                            "ceph-client");

		/*
		 * Create a user
		 */
		Map requestArgs = new HashMap();
		requestArgs.put("access", "usage=read, write; users=read, write");
		requestArgs.put("display-name", "New User");
		requestArgs.put("email", "new-user@email.com");
		requestArgs.put("format", "json");
		requestArgs.put("uid", "new-user");

		CloseableHttpResponse response =
			adminApi.execute("PUT", "/admin/user", null, requestArgs);

		System.out.println(response.getStatusLine());
		HttpEntity entity = response.getEntity();

		try {
			System.out.println("\nResponse Content is: "
				+ EntityUtils.toString(entity, "UTF-8") + "\n");
			response.close();
		} catch (IOException e){
			System.err.println ("Encountered an I/O exception.");
			e.printStackTrace();
		}

		/*
		 * Get a user
		 */
		requestArgs = new HashMap();
		requestArgs.put("format", "json");
		requestArgs.put("uid", "new-user");

		response = adminApi.execute("GET", "/admin/user", null, requestArgs);

		System.out.println(response.getStatusLine());
		entity = response.getEntity();

		try {
			System.out.println("\nResponse Content is: "
				+ EntityUtils.toString(entity, "UTF-8") + "\n");
			response.close();
		} catch (IOException e){
			System.err.println ("Encountered an I/O exception.");
			e.printStackTrace();
		}

		/*
		 * Modify a user
		 */
		requestArgs = new HashMap();
		requestArgs.put("display-name", "John Doe");
		requestArgs.put("email", "johndoe@email.com");
		requestArgs.put("format", "json");
		requestArgs.put("uid", "new-user");
		requestArgs.put("max-buckets", "100");

		response = adminApi.execute("POST", "/admin/user", null, requestArgs);

		System.out.println(response.getStatusLine());
		entity = response.getEntity();

		try {
			System.out.println("\nResponse Content is: "
				+ EntityUtils.toString(entity, "UTF-8") + "\n");
			response.close();
		} catch (IOException e){
			System.err.println ("Encountered an I/O exception.");
			e.printStackTrace();
		}


		/*
		 * Create a subuser
		 */
		requestArgs = new HashMap();
		requestArgs.put("format", "json");
		requestArgs.put("uid", "new-user");
		requestArgs.put("subuser", "foobar");

		response = adminApi.execute("PUT", "/admin/user", "subuser", requestArgs);
		System.out.println(response.getStatusLine());
		entity = response.getEntity();

		try {
			System.out.println("\nResponse Content is: "
				+ EntityUtils.toString(entity, "UTF-8") + "\n");
			response.close();
		} catch (IOException e){
			System.err.println ("Encountered an I/O exception.");
			e.printStackTrace();
		}


		/*
		 * Delete a user
		 */
		requestArgs = new HashMap();
		requestArgs.put("format", "json");
		requestArgs.put("uid", "new-user");

		response = adminApi.execute("DELETE", "/admin/user", null, requestArgs);
		System.out.println(response.getStatusLine());
		entity = response.getEntity();

		try {
			System.out.println("\nResponse Content is: "
				+ EntityUtils.toString(entity, "UTF-8") + "\n");
			response.close();
		} catch (IOException e){
			System.err.println ("Encountered an I/O exception.");
			e.printStackTrace();
		}
	}
}

추가 리소스