背景
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) {
}
以上两部分代码均可用,但具体使用的依赖版本有所不同,文中给出的依赖为第一部分代码使用。另,由于第二部分代码历时已久,具体出处已不可查,如有侵权请联系我删除或知道出处的可以联系我注明出处,谢谢。