1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/xixifeng.com-httpsign

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
README.md 19 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 27.11.2024 22:27 95a3d93

Введение

Добро пожаловать в использование HTTP Sign.
Этот проект решает следующие проблемы в области HTTP-коммуникаций:

  • Предотвращение повторных атак;
  • Предотвращение подмены данных в процессе передачи;
  • Обеспечение идемпотентности запросов к сервисам.

Таким образом, HTTP Sign делает HTTP-коммуникации максимально безопасными.

Использование

<dependency>
    <groupId>org.fastquery</groupId>
    <artifactId>httpsign</artifactId>
    <version>1.0.5</version>
</dependency>

Подготовка JAX-RS Resource Classes

@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";
  }
  
}

Написание ContainerRequestFilter для сервера

@org.fastquery.httpsign.Authorization
public class AuthorizationContainerRequestFilter extends 
        org.fastquery.httpsign.AuthAbstractContainerRequestFilter {
    @Override
    public String getAccessKeySecret(String accessKeyId) {
        // Находит accessKeySecret по accessKeyId
    }
}

Написание ClientRequestFilter для клиента

public class AuthorizationClientRequestFilter extends 
        org.fastquery.httpsign.AuthAbstractClientRequestFilter {
    @Override
    public String getAccessKeySecret(String accessKeyId) {
        // Находит accessKeySecret по accessKeyId
    }
}

Использование в среде Jersey

Сервер:

@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");
// ... ...

Использование в среде CXF+Spring

Сервер:

<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>

Дизайн HTTP Sign

Условные обозначения

Условное обозначение Значение
< > Переменная
[ ] Необязательный элемент
{ } Обязательный элемент
| Взаимоисключающие элементы
Знаки пунктуации В тексте используются только английские знаки пунктуации.

Правила именования параметров запроса

  1. Первая буква — строчная, если параметр состоит из нескольких слов, то первая буква каждого последующего слова должна быть прописной (например, userInfo).
  2. Аббревиатуры всегда пишутся строчными буквами (например, vcd).
  3. Параметр может состоять только из символов [A~Z], [a~z], [0~9] и знаков «-», «_», «.».
  4. Параметр не может начинаться с цифры.
  5. Запрещено использовать китайские иероглифы и пиньинь в именах параметров.

Таблица терминов

Термин Полное название Русский перевод Описание
RS RESTful Web Services Веб-сервисы REST Сервисы, построенные по архитектуре REST.
SecurityGroup Security Group Группа безопасности Определяет политику безопасности.
GMT Greenwich Mean Time Всемирное время по Гринвичу Время, установленное на Королевской обсерватории в Гринвиче.
URIPath Uniform Resource Identifier Path Путь унифицированного идентификатора ресурса Используется для идентификации пути к определённому интернет-ресурсу.
RFC Request For Comments Серия документов с номерами Почти все стандарты интернета включены в RFC.

Объяснение терминов

  1. Лексикографический порядок
    Сортировка слов как в словаре, от первой буквы к последующим, при совпадении первых букв сравниваются вторые буквы и так далее.
    Например, «scheme , java , basic , sql , php» после лексикографической сортировки будет выглядеть как «basic , java , php , scheme , sql».

  2. Идемпотентность
    Интерфейс спроектирован таким образом, что один и тот же URL можно вызывать повторно без изменения результата.

Ограничения использования

Разница между текущим временем на стороне клиента и текущим временем на сервере не должна превышать 10 минут, иначе запрос будет отклонён. Это означает, что время на клиенте не должно опережать или отставать от времени на сервере более чем на 10 минут.

Структура запроса

  1. Адрес сервиса
    Интерфейсы разделены на разные функциональные модули, каждый модуль использует свой домен или контекст доступа, конкретные домены или контексты см. в документации по каждому интерфейсу.

  2. Протокол связи
    Все интерфейсы используют HTTPS для связи.

  3. Метод запроса
    Поддерживаются методы [GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS].

  4. Кодировка символов
    Если не указано иное, используется кодировка UTF-8.

  5. Структура запроса API

    Название Описание Примечание
    Вход в API Входная точка для вызова API-сервисов RS Например, https://<домен>/путь/привет
    Общий заголовок Общие заголовки запросов, используемые во всех интерфейсах См. раздел Общие параметры
    Общие параметры Общие параметры, используемые во всех интерфейсах См. раздел Общие параметры

Все HTTP-заголовки, которые начинаются с «X-Custom-», называются пользовательскими заголовками запроса. Правила построения BuildCustomHeaders следующие:

  1. Все имена HTTP-запросов, начинающиеся с «X-Custom-» преобразуются в нижний регистр, например, «X-Custom-Meta-Author: FastQuery» становится «x-custom-meta-author: FastQuery».

  2. Полученные на предыдущем шаге заголовки запросов сортируются в алфавитном порядке.

  3. Между именем и содержимым заголовка запроса ставится символ двоеточия, а пробелы слева и справа от него удаляются. Например, «x-custom-meta-author : FastQuery» превращается в «x-custom-meta-author:FastQuery».

  4. Каждый полный заголовок (имя: содержимое) отделяется от следующего символом новой строки, после чего все они объединяются в BuildCustomHeaders.

  5. 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

Правила построения:

  • 9.1. Сортировка параметров. Все параметры запроса сортируются по алфавиту. Фактически это означает сортировку по ASCII-коду от меньшего к большему.
Буква 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.

  • 9.2. Кодирование параметров. Значения упорядоченных параметров запроса кодируются с использованием URL-кодирования. При этом соблюдаются следующие правила:
    • 9.2.1. Значения параметров кодируются с помощью UTF-8.
    • 9.2.2. Символы A–Z, a–z, 0–9, а также символы «-», «_», «.» и «~» не кодируются.
    • 9.2.3. Для остальных символов используется процентное кодирование согласно RFC3986. Это означает, что каждый байт представляется в формате «%XY», где XY — шестнадцатеричное представление байта. Например, для пробела « », который в UTF-8 представлен байтом 0X22, URL-кодом будет %22; для символа «*», представленного байтом 0X2A, URL-код — %2A.
    • 9.2.4. Для расширенных символов UTF-8 используется формат %AB%CD. Первые 128 символов ASCII представлены одним байтом. Однако для символов выше 127 требуется второй байт. Эти два байта вместе определяют один символ. Например:
      Символ UTF-8 URL-код
      α 0XCEB1 %CE%B1
      β 0XCEB2 %CE%B2
      γ 0XCEB3 %CE%B3
    • 9.2.5. При использовании инструментов кодирования следует учитывать некоторые особенности. В частности, стандартный Java-метод java.net.URLEncoder реализует алгоритм кодирования application/x-www-form-urlencoded MIME. Например, 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 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/xixifeng.com-httpsign.git
git@api.gitlife.ru:oschina-mirror/xixifeng.com-httpsign.git
oschina-mirror
xixifeng.com-httpsign
xixifeng.com-httpsign
master