Добро пожаловать в использование HTTP Sign.
Этот проект решает следующие проблемы в области HTTP-коммуникаций:
Таким образом, HTTP Sign делает HTTP-коммуникации максимально безопасными.
<dependency>
<groupId>org.fastquery</groupId>
<artifactId>httpsign</artifactId>
<version>1.0.5</version>
</dependency>
@javax.ws.rs.Path("helloworld")
public class HelloWorldResource {
@org.fastquery.httpsign.Authorization // Действует на метод, делая его аутентифицированным
@javax.ws.rs.GET
@javax.ws.rs.Produces("text/plain")
public String getHello() {
return "hi";
}
}
@org.fastquery.httpsign.Authorization
public class AuthorizationContainerRequestFilter extends
org.fastquery.httpsign.AuthAbstractContainerRequestFilter {
@Override
public String getAccessKeySecret(String accessKeyId) {
// Находит accessKeySecret по accessKeyId
}
}
public class AuthorizationClientRequestFilter extends
org.fastquery.httpsign.AuthAbstractClientRequestFilter {
@Override
public String getAccessKeySecret(String accessKeyId) {
// Находит accessKeySecret по accessKeyId
}
}
Сервер:
@ApplicationPath("userResorce")
public class Application extends ResourceConfig {
public Application() throws IOException {
register(HelloWorldResource.class);
register(AuthorizationContainerRequestFilter.class);
}
}
Клиент JAX-RS:
javax.ws.rs.client.Client client = javax.ws.rs.client.ClientBuilder.newClient();
client.register(AuthorizationClientRequestFilter.class);
javax.ws.rs.client.WebTarget target = client.target("http://localhost:8080").path("userResorce/helloworld");
// ... ...
Сервер:
<jaxrs:server address="http://localhost...">
<jaxrs:serviceBeans>
<bean class="your package.HelloWorldResource" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="org.fastquery.httpsign.sample.AuthorizationContainerRequestFilter" />
</jaxrs:providers>
</jaxrs:server>
Клиент:
<jaxrs:client address="<your request address>" serviceClass="<your request service>">
<jaxrs:providers>
<bean class="org.fastquery.httpsign.sample.AuthorizationClientRequestFilter" />
</jaxrs:providers>
</jaxrs:client>
Условное обозначение | Значение |
---|---|
< > | Переменная |
[ ] | Необязательный элемент |
{ } | Обязательный элемент |
| | Взаимоисключающие элементы |
Знаки пунктуации | В тексте используются только английские знаки пунктуации. |
Термин | Полное название | Русский перевод | Описание |
---|---|---|---|
RS |
RESTful Web Services | Веб-сервисы REST | Сервисы, построенные по архитектуре REST. |
SecurityGroup |
Security Group | Группа безопасности | Определяет политику безопасности. |
GMT |
Greenwich Mean Time | Всемирное время по Гринвичу | Время, установленное на Королевской обсерватории в Гринвиче. |
URIPath |
Uniform Resource Identifier Path | Путь унифицированного идентификатора ресурса | Используется для идентификации пути к определённому интернет-ресурсу. |
RFC |
Request For Comments | Серия документов с номерами | Почти все стандарты интернета включены в RFC. |
Лексикографический порядок
Сортировка слов как в словаре, от первой буквы к последующим, при совпадении первых букв сравниваются вторые буквы и так далее.
Например, «scheme , java , basic , sql , php» после лексикографической сортировки будет выглядеть как «basic , java , php , scheme , sql».
Идемпотентность
Интерфейс спроектирован таким образом, что один и тот же URL можно вызывать повторно без изменения результата.
Разница между текущим временем на стороне клиента и текущим временем на сервере не должна превышать 10 минут, иначе запрос будет отклонён. Это означает, что время на клиенте не должно опережать или отставать от времени на сервере более чем на 10 минут.
Адрес сервиса
Интерфейсы разделены на разные функциональные модули, каждый модуль использует свой домен или контекст доступа, конкретные домены или контексты см. в документации по каждому интерфейсу.
Протокол связи
Все интерфейсы используют HTTPS для связи.
Метод запроса
Поддерживаются методы [GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS].
Кодировка символов
Если не указано иное, используется кодировка UTF-8.
Структура запроса API
Название | Описание | Примечание |
---|---|---|
Вход в API | Входная точка для вызова API-сервисов RS | Например, https://<домен>/путь/привет |
Общий заголовок | Общие заголовки запросов, используемые во всех интерфейсах | См. раздел Общие параметры |
Общие параметры | Общие параметры, используемые во всех интерфейсах | См. раздел Общие параметры |
Все HTTP-заголовки, которые начинаются с «X-Custom-», называются пользовательскими заголовками запроса. Правила построения BuildCustomHeaders следующие:
Все имена HTTP-запросов, начинающиеся с «X-Custom-» преобразуются в нижний регистр, например, «X-Custom-Meta-Author: FastQuery» становится «x-custom-meta-author: FastQuery».
Полученные на предыдущем шаге заголовки запросов сортируются в алфавитном порядке.
Между именем и содержимым заголовка запроса ставится символ двоеточия, а пробелы слева и справа от него удаляются. Например, «x-custom-meta-author : FastQuery» превращается в «x-custom-meta-author:FastQuery».
Каждый полный заголовок (имя: содержимое) отделяется от следующего символом новой строки, после чего все они объединяются в BuildCustomHeaders.
BuildCustomHeaders допускает пустую строку («»).
Пример: если есть один заголовок «X-CUSTOM-META-A:xx», то BuildCustomHeaders будет «x-custom-meta-a:xx». Если есть два заголовка «X-CUSTOM-META-A:xx» и «X-CUSTOM-META-B:yy», то BuildCustomHeaders — «x-custom-meta-a:xx\nx-custom-meta-b:yy».
8. URIPath
Адрес между портом URL и QueryString, без символа «?», называется URIPath. Пример: если у нас есть URL-адрес «https://<домен><по умолчанию 80 можно опустить>/path/hi?action=myInfo», то URIPath будет «/path/hi». Если у нас есть URL «https://<домен>:8080/path/hi?action=myInfo», то URIPath — «/path/hi». Если у нас есть URL «https://<домен>:8080/path/hi», то URIPath также «/path/hi». Если у нас есть URL «https://<домен>:8080», то URIPath равен «/». Если у нас есть URL «https://<домен>:8080?», то URIPath пуст.
В качестве примера на Java получение URIPath:
public class AuthorizationClientRequestFilter implements javax.ws.rs.client.ClientRequestFilter {
@Override
public void filter(javax.ws.rs.client.ClientRequestContext requestContext) {
java.net.URI uri = requestContext.getUri();
String uriPath = uri.getPath();
LOG.debug("uriPath:{}",uriPath);
}
}
9. BuildRequestParameters
Правила построения:
Буква | ASCII-код |
---|---|
A | 65 |
N | 78 |
R | 82 |
S | 83 |
T | 84 |
i | 105 |
l | 108 |
o | 111 |
После сортировки по возрастанию получаем порядок: A, N, R, S, T, i, l, o.
Символ | UTF-8 | URL-код |
---|---|---|
α | 0XCEB1 | %CE%B1 |
β | 0XCEB2 | %CE%B2 |
γ | 0XCEB3 | %CE%B3 |
URLEncoder.encode("~", "utf-8")
возвращает результат %7E, хотя RFC3986 не требует кодирования символа ~. URLEncoder.encode("*", "utf-8")
выдаёт * (RFC3986 также не требует кодирования *). URLEncoder.encode(" ", "utf-8")
возвращает +, но RFC3986 требует использования формата %XY. Поэтому при использовании java.net.URLEncoder необходимо дополнительно обрабатывать результаты, заменяя + на %20, * на %2A и %7E на ~. Пример построения запроса
9.3. Объединение параметров
После того, как значения параметров были закодированы в соответствии с RFC3986, имена параметров и их значения соединяются знаком =
, а параметры отделяются друг от друга знаком &
. На этом этапе построение BuildRequestParameters завершается.
9.4 Пример:
Предположим, что есть шесть параметров:
{
"nonce" : "1aabcde-5268-3326-c845-56kljgwexe",
"action" : "myInfo",
"offset" : 1,
"secretKeyId" : "BKJGW40598092JXMWNRF",
"limit" : 15
}
Шаг 1: Параметры сортируются по словарю.
{
"action" : "myInfo",
"limit" : 15,
"nonce" : "1aabcde-5268-3326-c845-56kljgwexe",
"offset" : 1,
"secretKeyId" : "BKJGW40598092JXMWNRF"
}
Шаг 2: Значения параметров кодируются в соответствии с RFC3986.
Шаг 3: Объединение параметров.
action=myInfo&limit=15&nonce=1aabcde-5268-3326-c845-56kljgwexe&offset=1&secretKeyId=BKJGW40598092JXMWNRF — это и есть BuildRequestParameters.
Пример расчёта Authorization
Пусть AccessKeySecret будет равен «KYA8A4-74E17B58B093», алгоритм подписи — «HMACSHA1», URIPath — «/httpsign/userResorce/greet», метод запроса (Request Method) — POST, заголовок запроса:
Заголовок | Значение |
---|---|
Authorization | Вычисляется |
Accept | application/json |
Date | Wed, 11 Apr 2018 06:03:43 GMT |
X-Custom-Meta-Author | FastQuery.HttpSign |
X-Custom-Meta-Description | HTTP authentication techniques. |
X-Custom-Meta-Range | 52363 |
Пусть запрос содержит следующие параметры:
Параметр | Значение |
---|---|
accessKeyId | AP084671DF-5F8C-41D2 |
typeId | 7 |
nonce | e6e03b6f-7de2-4d02-8e04-3ccbad143389 |
Запрос Body: «蚓无爪牙之利,筋骨之强,上食埃土,下饮黄泉,用心一也».
Решение:
Это решение описывает процесс вычисления Authorization для упрощения понимания читателем, поэтому код представлен в сжатом виде.
// 密钥
String accessKeySecret = "KYA8A4-74E17B58B093";
String uriPath = "/httpsign/userResorce/greet";
String httpMethod = "POST";
String accept = "application/json";
String date = "Wed, 11 Apr 2018 06:03:43 GMT";
// Создание заголовка запроса
java.util.TreeMap<String, String> headerTreeMap = new java.util.TreeMap<>();
headerTreeMap.put("X-Custom-Content-Range", "52363");
headerTreeMap.put("X-Custom-Meta-Author", "FastQuery.HttpSign");
headerTreeMap.put("X-Custom-Meta-Description", "HTTP authentication techniques.");
StringBuilder headersBuilder = new StringBuilder();
headerTreeMap.forEach((k, v) -> headersBuilder.append(k.toLowerCase()).append(':').append(v).append('\n'));
String headersStr = headersBuilder.toString();
// Создание параметров запроса
java.util.TreeMap<String, String> queryStringTreeMap = new java.util.TreeMap<>();
queryStringTreeMap.put("accessKeyId", "AP084671DF-5F8C-41D2");
queryStringTreeMap.put("typeId", "7");
queryStringTreeMap.put("nonce", "e6e03b6f-7de2-4d02-8e04-3ccbad143389");
StringBuilder requestParametersBuilder = new StringBuilder();
queryStringTreeMap.forEach((k, v) -> {
try {
requestParametersBuilder.append('&').append(k).append('=')
.append(java.net.URLEncoder.encode(v, "utf-8").replace("+", "%20")
.replace("*", "%2A").replace("%7E", "~"));
} catch (java.io.UnsupportedEncodingException e) {
throw new RuntimeException("URL编码出错", e);
}
});
String requestParameters = requestParametersBuilder.substring(1);
// Вычисление Content-MD5
String requestBody = "蚓无爪牙之利,筋骨之强,上食埃土,下饮黄泉,用心一也";
byte[] input = requestBody.getBytes(java.nio.charset.Charset.forName("utf-8"));
java.security.MessageDigest messageDigest = java.security.MessageDigest.getInstance("MD5");
messageDigest.update(input);
byte[] md5Bytes = messageDigest.digest();
String contentMD5 = java.util.Base64.getEncoder().encodeToString(md5Bytes);
// Построение stringToSign
StringBuilder sb = new StringBuilder();
sb.append(httpMethod).append('\n');
sb.append(contentMD5).append('\n');
sb.append(accept).append('\n');
sb.append(date).append('\n');
sb.append(headersStr);
sb.append(uriPath).append('\n');
sb.append(requestParameters);
String stringToSign = sb.toString();
// Вычисление signature
javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HMACSHA1");
mac.init(new javax.crypto.spec.SecretKeySpec(accessKeySecret.getBytes(
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )