AWS API签名
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package name.chengchao.springrun.util; | |
import java.net.URL; | |
import java.util.Calendar; | |
import java.util.Date; | |
import java.util.Map; | |
import java.util.TreeMap; | |
import java.util.stream.Collectors; | |
import org.apache.commons.codec.binary.Hex; | |
import org.apache.commons.codec.digest.DigestUtils; | |
import org.apache.commons.lang3.StringUtils; | |
import org.apache.commons.lang3.time.DateFormatUtils; | |
public class AwsAPIAuth { | |
private final static String AccessKey = "***************"; | |
private final static String SecretKey = "***************"; | |
public static void main(String[] args) throws Exception { | |
listIAMUsers(); | |
// createGroup(); | |
} | |
public static void listIAMUsers() throws Exception { | |
String region = "us-east-1"; | |
String httpMethod = "GET"; | |
String url = "https://iam.amazonaws.com/"; | |
Map<String, String> urlParams = new TreeMap<>(); | |
urlParams.put("Action", "ListUsers"); | |
urlParams.put("Version", "2010-05-08"); | |
doRequest(region, httpMethod, url, urlParams, null); | |
} | |
public static void createGroup() throws Exception { | |
String region = "us-east-1"; | |
String httpMethod = "POST"; | |
String url = "https://iam.amazonaws.com/"; | |
Map<String, String> urlParams = new TreeMap<>(); | |
String body = "Action=CreateGroup&Version=2010-05-08&GroupName=charlesTestGroup1941"; | |
doRequest(region, httpMethod, url, urlParams, body); | |
} | |
public static String doRequest(String region, String httpMethod, String url, Map<String, String> urlParams, | |
String body) | |
throws Exception { | |
// 容忍度是30分钟,+-15分钟 | |
// Signature expired: 20221229T051026Z is now earlier than 20221229T063528Z (20221229T065028Z - 15 min.) | |
// Signature not yet current: 20221229T071139Z is still later than 20221229T070641Z (20221229T065141Z + 15 min.) | |
Calendar calendar = Calendar.getInstance(); | |
// calendar.add(Calendar.MINUTE, 20); | |
Date now = calendar.getTime(); | |
String datetime = DateFormatUtils.formatUTC(now, "yyyyMMdd'T'HHmmss'Z'"); | |
String date = DateFormatUtils.formatUTC(now, "yyyyMMdd"); | |
String host = new URL(url).getHost(); | |
String service = host.substring(0, host.indexOf('.')); | |
Map<String, String> headerMap = new TreeMap<>(); | |
headerMap.put("X-Amz-Date", datetime); | |
headerMap.put("Host", host); | |
// default response is xml format | |
// headerMap.put("Accept", "application/json"); | |
// if ("POST".equals(httpMethod) && StringUtils.isNotBlank(body)) { | |
// headerMap.put("X-Amz-Content-Sha256", DigestUtils.sha256Hex(body)); | |
// } | |
String headerList = headerMap.keySet().stream().map(String::toLowerCase).collect(Collectors.joining(";")); | |
// step1:request structure | |
String requestString = buildCanonicalRequest(httpMethod, url, urlParams, headerMap, body); | |
// step2:request structure hash (sha256Hex) | |
String requestHashHex = DigestUtils.sha256Hex(requestString); | |
// step3:string to sign | |
String scope = date + "/" + region + "/" + service + "/aws4_request"; | |
String stringToSign = buildStringToSign(datetime, scope, requestHashHex); | |
// step4:sign code | |
String sign = sign(SecretKey, date, region, service, stringToSign); | |
// step5:authorization header | |
String authorization = buildAuthorization(AccessKey, scope, headerList, sign); | |
headerMap.put("Authorization", authorization); | |
System.out.println("--------------1. request structure---------------------------------"); | |
System.out.println(requestString); | |
System.out.println("--------------2. request structure hash (sha256Hex)----------------"); | |
System.out.println(requestHashHex); | |
System.out.println("--------------3. string to sign------------------------------------"); | |
System.out.println(stringToSign); | |
System.out.println("--------------4. sign code-----------------------------------------"); | |
System.out.println(sign); | |
System.out.println("--------------5. authorization header------------------------------"); | |
System.out.println(authorization); | |
System.out.println("==============response============================================="); | |
String result = null; | |
if ("GET".equals(httpMethod)) { | |
result = OkHttpClientUtils.get(url, urlParams, headerMap); | |
} else if ("POST".equals(httpMethod)) { | |
result = OkHttpClientUtils.post(url, headerMap, body, "application/x-www-form-urlencoded"); | |
} | |
System.out.println(result); | |
return result; | |
} | |
/** | |
* 第一步: 构建请求体 | |
* | |
* @param httpMethod | |
* @param url | |
* @param urlParams | |
* @param headerMap | |
* @param body | |
* @return | |
* @throws Exception | |
*/ | |
public static String buildCanonicalRequest(String httpMethod, String url, Map<String, String> urlParams, | |
Map<String, String> headerMap, String body) throws Exception { | |
StringBuilder sb = new StringBuilder(); | |
sb.append(httpMethod); | |
sb.append("\n"); | |
sb.append(new URL(url).getPath()); | |
sb.append("\n"); | |
if (urlParams != null && !urlParams.isEmpty()) { | |
for (Map.Entry<String, String> entry : urlParams.entrySet()) { | |
sb.append(entry.getKey()); | |
sb.append("="); | |
sb.append(entry.getValue()); | |
sb.append("&"); | |
} | |
sb.deleteCharAt(sb.lastIndexOf("&")); | |
} | |
sb.append("\n"); | |
if (headerMap != null && !headerMap.isEmpty()) { | |
String headerList = headerMap.keySet().stream().map(String::toLowerCase).collect(Collectors.joining(";")); | |
for (Map.Entry<String, String> entry : headerMap.entrySet()) { | |
sb.append(StringUtils.lowerCase(entry.getKey())); | |
sb.append(":"); | |
sb.append(entry.getValue()); | |
sb.append("\n"); | |
} | |
sb.append("\n"); | |
sb.append(headerList); | |
sb.append("\n"); | |
} | |
if (StringUtils.isBlank(body)) { | |
body = ""; | |
} | |
// add sha256hex payload | |
sb.append(DigestUtils.sha256Hex(body)); | |
return sb.toString(); | |
} | |
/** | |
* 第二步: 构建签名体 | |
* | |
* @param datetime | |
* @param scope 请求范围 | |
* @param requestHashHex 请求体的hash摘要sha-256 | |
* @return | |
*/ | |
public static String buildStringToSign(String datetime, String scope, String requestHashHex) { | |
StringBuilder sb = new StringBuilder(); | |
sb.append("AWS4-HMAC-SHA256"); | |
sb.append("\n"); | |
sb.append(datetime); | |
sb.append("\n"); | |
sb.append(scope); | |
sb.append("\n"); | |
sb.append(requestHashHex); | |
return sb.toString(); | |
} | |
/** | |
* 第三步: 签名 | |
* | |
* @param secretKey | |
* @param date | |
* @param region | |
* @param service | |
* @param stringToSign | |
* @return | |
* @throws Exception | |
*/ | |
public static String sign(String secretKey, String date, String region, String service, String stringToSign) | |
throws Exception { | |
byte[] kSecret = ("AWS4" + secretKey).getBytes("UTF8"); | |
byte[] kDate = HmacUtils.hmacSHA256(kSecret, date); | |
byte[] kRegion = HmacUtils.hmacSHA256(kDate, region); | |
byte[] kService = HmacUtils.hmacSHA256(kRegion, service); | |
byte[] kSigning = HmacUtils.hmacSHA256(kService, "aws4_request"); | |
byte[] signByte = HmacUtils.hmacSHA256(kSigning, stringToSign); | |
String sign = Hex.encodeHexString(signByte); | |
return sign; | |
} | |
/** | |
* 第四步: 构建 authorization header | |
* | |
* @param accessKey | |
* @param scope | |
* @param sign | |
* @return | |
*/ | |
public static String buildAuthorization(String accessKey, String scope, String headerList, String sign) { | |
StringBuilder authorizationBuilder = new StringBuilder(); | |
authorizationBuilder.append("AWS4-HMAC-SHA256"); | |
authorizationBuilder.append(" "); | |
authorizationBuilder.append("Credential=" + accessKey); | |
authorizationBuilder.append("/"); | |
authorizationBuilder.append(scope); | |
authorizationBuilder.append(","); | |
authorizationBuilder.append("SignedHeaders="); | |
authorizationBuilder.append(headerList); | |
authorizationBuilder.append(","); | |
authorizationBuilder.append("Signature="); | |
authorizationBuilder.append(sign); | |
return authorizationBuilder.toString(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package name.chengchao.springrun.util; | |
import java.util.HashMap; | |
import java.util.Map; | |
import java.util.concurrent.TimeUnit; | |
import okhttp3.Call; | |
import okhttp3.FormBody; | |
import okhttp3.Headers; | |
import okhttp3.HttpUrl; | |
import okhttp3.MediaType; | |
import okhttp3.OkHttpClient; | |
import okhttp3.Request; | |
import okhttp3.RequestBody; | |
import okhttp3.Response; | |
/** | |
* OkHttp client | |
* | |
* @author charles | |
* | |
*/ | |
public class OkHttpClientUtils { | |
// public static final MediaType MediaType_JSON = MediaType.parse("application/json; charset=utf-8"); | |
public static OkHttpClient client = new OkHttpClient.Builder() | |
.connectTimeout(10, TimeUnit.SECONDS) | |
.writeTimeout(10, TimeUnit.SECONDS) | |
.readTimeout(40, TimeUnit.SECONDS) | |
.callTimeout(60, TimeUnit.SECONDS) | |
.addInterceptor(new SimpleLoggingInterceptor()) | |
.build(); | |
public static enum HttpMethod { | |
GET, | |
POST, | |
PUT, | |
PATCH, | |
DELETE | |
} | |
public static void main(String[] args) throws Exception { | |
String url = "https://www.chengchao.name"; | |
System.out.println(get(url, null, null)); | |
} | |
public static String get(String url, Map<String, String> urlParams, Map<String, String> headerMap) | |
throws Exception { | |
HttpUrl.Builder httpUrl = HttpUrl.parse(url).newBuilder(); | |
if (null != urlParams && !urlParams.isEmpty()) { | |
for (Map.Entry<String, String> entry : urlParams.entrySet()) { | |
httpUrl.addQueryParameter(entry.getKey(), entry.getValue()); | |
} | |
} | |
String result = excute(httpUrl.build(), HttpMethod.GET, headerMap, null); | |
return result; | |
} | |
public static String post(String url, Map<String, String> headerMap, String jsonBody) | |
throws Exception { | |
HttpUrl.Builder httpUrl = HttpUrl.parse(url).newBuilder(); | |
String result = | |
excute(httpUrl.build(), HttpMethod.POST, headerMap, jsonBody, "application/json; charset=utf-8"); | |
return result; | |
} | |
public static String post(String url, Map<String, String> headerMap, String body, String mediaType) | |
throws Exception { | |
HttpUrl.Builder httpUrl = HttpUrl.parse(url).newBuilder(); | |
String result = excute(httpUrl.build(), HttpMethod.POST, headerMap, body, mediaType); | |
return result; | |
} | |
public static String post(String url, Map<String, String> headerMap, Map<String, String> formBody) | |
throws Exception { | |
HttpUrl.Builder httpUrl = HttpUrl.parse(url).newBuilder(); | |
String result = excute(httpUrl.build(), HttpMethod.POST, headerMap, formBody); | |
return result; | |
} | |
public static String put(String url, Map<String, String> headerMap, String jsonBody) | |
throws Exception { | |
HttpUrl.Builder httpUrl = HttpUrl.parse(url).newBuilder(); | |
String result = excute(httpUrl.build(), HttpMethod.PUT, headerMap, jsonBody); | |
return result; | |
} | |
public static String delete(String url, Map<String, String> headerMap) | |
throws Exception { | |
HttpUrl.Builder httpUrl = HttpUrl.parse(url).newBuilder(); | |
String result = excute(httpUrl.build(), HttpMethod.DELETE, headerMap, null); | |
return result; | |
} | |
public static String patch(String url, Map<String, String> headerMap, String body) | |
throws Exception { | |
HttpUrl.Builder httpUrl = HttpUrl.parse(url).newBuilder(); | |
String result = excute(httpUrl.build(), HttpMethod.PATCH, headerMap, body); | |
return result; | |
} | |
public static String excute(HttpUrl httpUrl, HttpMethod method, Map<String, String> headerMap, Object body) | |
throws Exception { | |
return excute(httpUrl, method, headerMap, body, null); | |
} | |
@SuppressWarnings("unchecked") | |
public static String excute(HttpUrl httpUrl, HttpMethod method, Map<String, String> headerMap, Object body, | |
String mediaType) | |
throws Exception { | |
Headers.Builder headers = new Headers.Builder(); | |
if (null != headerMap && !headerMap.isEmpty()) { | |
for (Map.Entry<String, String> entry : headerMap.entrySet()) { | |
headers.add(entry.getKey(), entry.getValue()); | |
} | |
} | |
RequestBody requestBody = null; | |
if (null != body) { | |
if (body instanceof String) { | |
String bodyString = (String)body; | |
requestBody = RequestBody.create(bodyString, MediaType.parse(mediaType)); | |
} else if (body instanceof HashMap) { | |
Map<String, String> bodyMap = (Map<String, String>)body; | |
FormBody.Builder formBody = new FormBody.Builder(); | |
for (Map.Entry<String, String> entry : bodyMap.entrySet()) { | |
formBody.add(entry.getKey(), entry.getValue()); | |
} | |
requestBody = formBody.build(); | |
} | |
} | |
Request request = new Request(httpUrl, method.toString(), headers.build(), requestBody, new HashMap<>()); | |
Call call = client.newCall(request); | |
Response response = call.execute(); | |
int responseCode = response.code(); | |
String result = response.body().string(); | |
// 2xx 正常 | |
if (!(200 <= responseCode && 300 > responseCode)) { | |
throw new RuntimeException("[" + responseCode + "] " + result); | |
} | |
return result; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment