AWS V4鑑權之ElasticSearch
阿新 • • 發佈:2018-12-26
背景
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) {
}
以上兩部分程式碼均可用,但具體使用的依賴版本有所不同,文中給出的依賴為第一部分程式碼使用。另,由於第二部分程式碼歷時已久,具體出處已不可查,如有侵權請聯絡我刪除或知道出處的可以聯絡我註明出處,謝謝。