天天看點

AWS V4鑒權之ElasticSearch背景内容

背景

Amazon簽名版本4是将身份驗證資訊添加到HTTP發送的AWS請求的過程。為了安全起見,大多數對AWS的請求必須使用通路密鑰進行簽名,該通路密鑰由通路密鑰ID和秘密通路密鑰組成。

在使用AWS的ElasticSearch服務時需要對請求鑒權,需要将你的

aws_access_key_id

aws_secret_access_key

作為請求的安全憑證進行安全校驗。本文講述的是進行Web開發時,如何在Java中調用相關的API進行驗證并向ES進行請求。

注:關于AWS V4鑒權的詳細資料,請查閱官方文檔:AWS V4 Signature Documentation

内容

由于之前在開發ES這一塊的時候,同僚在網上找到了一段驗證的代碼,在初期開發時使用了一段時間,但上線前需要進行修改。然後我接到這個任務之後,通過查閱一些資料,對這部分的代碼進行了重寫。代碼及解釋如下:

注意:以下兩部分代碼中的payload為請求ES拼接成的JSON格式的參數,詳細格式請參見ES官網

// 建構一個map,用來存放ES請求的頭資訊
Map<String, String> headers = new TreeMap<>();
headers.put("Content-Type", "application/json; charset=UTF-8");

// 建構一個請求對象
Request<Void> request = new DefaultRequest<Void>("es");
request.setHttpMethod(HttpMethodName.POST);
request.setEndpoint(URI.create(url));
request.setHeaders(headers);
request.setContent(new ByteArrayInputStream(payload.getBytes()));

// 建構一個signer對象,并設定其中的參數
AWS4Signer signer = new AWS4Signer();
// 服務所在的區域名,如:cn-northwest-1等
signer.setRegionName(region);
// 從建構的request對象中擷取服務名放入signer對象中
signer.setServiceName(request.getServiceName());
// 根據請求對象及accessKey, secretKey 進行簽名
signer.sign(request, new BasicAWSCredentials(accessKey, secretKey));

// 使用Amazon的HttpClient進行請求并擷取傳回
Response<String> rsp = new AmazonHttpClient(new ClientConfiguration())
      .requestExecutionBuilder()
      .executionContext(new ExecutionContext(true))
      .request(request)
      .errorResponseHandler(new HttpResponseHandler<SdkBaseException>() {
          @Override
          public SdkBaseException handle(com.amazonaws.http.HttpResponse httpResponse) throws Exception {
              System.out.println(httpResponse.getContent());
              return null;
          }

          @Override
          public boolean needsConnectionLeftOpen() {
              return false;
          }
      })
      .execute(new HttpResponseHandler<String>() {
          @Override
          public String handle(com.amazonaws.http.HttpResponse response) throws Exception {
              /* Get status code */
              int status = response.getStatusCode();
              if (status >= 200 && status < 300) {
                  ByteArrayOutputStream baos = new ByteArrayOutputStream();
                  int i = -1;
                  try {
                      while((i = response.getContent().read()) != -1){
                          baos.write(i);
                      }
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
                  return baos.toString();
              } else {
                  throw new ClientProtocolException("Unexpected response status: " + status);
              }
          }

          @Override
          public boolean needsConnectionLeftOpen() {
              return false;
          }
      });

// ESResult為封裝的ES搜尋結果對象,根據自己的需要進行修改
ESResult esResult = (ESResult) JSONUtility.jsonToObject(rsp.getAwsResponse(), ESResult.class);
return esResult;
           

以上就是ES進行請求并鑒權的代碼,下面是引入的ES及AWS的依賴包:

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-bom</artifactId>
    <version>1.11.63</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-core</artifactId>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>6.4.0</version>
</dependency>
           

然後就是之前在某篇部落格中找到的相關代碼,大家也可以做個參考:

TreeMap<String, String> awsHeaders = new TreeMap<String, String>();
 awsHeaders.put("host", host);

 AWSV4Auth aWSV4Auth = new AWSV4Auth.Builder(accessKey, secretKey)
         .regionName(region)
         .serviceName("es")
         .httpMethodName("POST")
         .canonicalURI(query)
         .queryParametes(null)
         .awsHeaders(awsHeaders)
         .payload(payload)
         .debug()
         .build();

 HttpPost httpPost = new HttpPost(url);
 StringEntity reqEntity = new StringEntity(payload, ContentType.APPLICATION_JSON);
 httpPost.setEntity(reqEntity);

 Map<String, String> header = aWSV4Auth.getHeaders();
 for (Map.Entry<String, String> entrySet : header.entrySet()) {
     String key = entrySet.getKey();
     String value = entrySet.getValue();

/* Attach header in your request */
     /* Simple get request */

     httpPost.addHeader(key, value);
 }
//        httpPostRequest(httpPost);
 CloseableHttpClient httpClient = HttpClients.createDefault();

/* Response handler for after request execution */
 ResponseHandler<String> responseHandler = new ResponseHandler<String>() {

     public String handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
         /* Get status code */
         int status = response.getStatusLine().getStatusCode();
         if (status >= 200 && status < 300) {
             /* Convert response to String */
             HttpEntity entity = response.getEntity();
             return entity != null ? EntityUtils.toString(entity) : null;
         } else {
             throw new ClientProtocolException("Unexpected response status: " + status);
         }
     }
 };
 ESResult esResult = null;
 try {
     String strResponse = httpClient.execute(httpPost, responseHandler);
     esResult = (ESResult) JSONUtility.jsonToObject(strResponse, ESResult.class);
 } catch (Exception e) {
 }

           
以上兩部分代碼均可用,但具體使用的依賴版本有所不同,文中給出的依賴為第一部分代碼使用。另,由于第二部分代碼曆時已久,具體出處已不可查,如有侵權請聯系我删除或知道出處的可以聯系我注明出處,謝謝。