Перейти к основному содержанию
`Ctrl`+`K`

# Аутентификация в REST/SOAP-сервисах

Сервисы используют HTTP-аутентификацию двух типов: Basic и Bearer.

## Basic — с использованием логина, пароля и имени базы

При Basic-аутентификации клиент должен передать в запросе:

- имя пользователя;

- пароль;

- имя базы.

Имя пользователя и пароль передаются через HTTP-заголовок `Authorization`.
Значение: `Basic {Base64Cred}`, где `{Base64Cred}` — строка `user:password`, закодированная в Base64.

Имя базы может быть передано (в порядке приоритета):

- через сегмент строки адреса. Пример: `http://server/{DATABASE}/`;

- через HTTP-заголовок `Database` со значением `{DATABASE}`;

- через HTTP-параметр `Database` со значением `{DATABASE}`. Пример: `http://server/?Database={DATABASE}`.

Где:
`{DATABASE}` — имя базы данных.

Если имя базы не передано ни одним из описанных способов, будет произведена попытка аутентификации с использованием имени базы по умолчанию. База по умолчанию определяется из конфигурационного файла `global3.config.xml` (в порядке приоритета):

- значение атрибута `<databases defaultDb="{DATABASE}"/>`;

- значение атрибута `<database alias="{DATABASE}"/>` первой базы в списке конфигураций `<databases/>`.

## Bearer — с использованием токена аутентификации

При Bearer-аутентификации клиент должен передать в запросе:

- токен аутентификации;

- имя базы.

    До версии AS 1.20 RC 15 включительно, доступны только токены, сформированные сервером приложений на основе: имени базы, имени пользователя и пароля.

    До версии AS 1.20 RC 15 включительно, указания имени базы при Bearer-аутентификации не требуется.

Запрос должен содержать HTTP-заголовок `Authorization`.
Значение `Bearer {Token}`, где `{Token}` — ключ авторизации, присвоенный пользователю можно получить в прикладном коде от сущности:

    session.user.token: String

Имя базы может быть передано (в порядке приоритета):

- Через сегмент строки адреса. Пример: `http://server/{DATABASE}/`;

- Через HTTP-заголовок `Database` со значением `{DATABASE}`;

- Через HTTP-параметр `Database` со значением `{DATABASE}`. Пример: `http://server/?Database={DATABASE}`.

Где:
`{DATABASE}` — имя базы данных.

Если имя базы не передано ни одним из описанных способов, будет произведена попытка аутентификации с использованием имени базы по умолчанию. База по умолчанию определяется из конфигурационного файла `global3.config.xml` (в порядке приоритета):

- Значение атрибута `<databases defaultDb="{DATABASE}"/>`;

- Значение атрибута `<database alias="{DATABASE}"/>` первой базы в списке конфигураций `<databases/>`.

## Типы токенов аутентификации

- `ast` — Application Server Token.

- `gjwt` — Gtk Json Web Token.

### Application Server Token

Токен формируется сервером приложений после входа в приложение Global с использованием имени пользователя и пароля. Токен возвращается клиенту в cookie `access_token`, привязанной к подмножеству адресов `http[s]://server/{DATABASE}/`. Эта cookie автоматически присоединяется ко всем запросам по адресам `http[s]://server/{DATABASE}/`, выполняемым из браузера, аутентифицированного в системе Global.

Токен устаревает после перезапуска сервера приложений.

### Gtk Json Web Token

Токен по стандарту JWT формируется в модуле GTK (или на внешнем сервисе) и валидируется в модуле GTK.

GJWT разделяются на подтипы:

- `UserHash` — Долгоживущий токен пользователя.

- `UserCrt` — Подписанный пользователем токен.

- `ProxyCrt` — Подписанный прокси-пользователем токен.

#### Долгоживущий токен пользователя

Формируется администратором системы Global и сообщается пользователю любым удобным способом, исключающим утечку токена.
Токен, сопоставленный с учётной записью пользователя, хранится в БД решения.
Срок годности определяется администратором.

Тело JWT (payload) должно содержать следующие поля:

- `typ` — `"UserHash"`.

- `sub` — имя пользователя, под которым необходимо аутентифицироваться.

- `exp` — дата устаревания.

При проверке валидности токена проверяется существование токена в списке сопоставленных с учётной записью токенов и дата устаревания.

#### Подписанный пользователем токен

Формируется пользователем и подписывается закрытым RSA-ключом пользователя.
Открытый RSA-ключ, сопоставленный с учётной записью пользователя, хранится в БД решения.
Срок годности определяется пользователем.

Тело JWT (payload) должно содержать следующие поля:

- `typ` — `"UserCrt"`.

- `sub` — имя пользователя, под которым необходимо аутентифицироваться.

- `cid` — идентификатор открытого ключа в системе Global, сопоставленного с пользователем.

- `exp` — дата устаревания.

При проверке валидности токена на основе полей `sub` и `cid` из БД решения получается открытый RSA-ключ и проверяется валидность подписи JWT.

#### Подписанный прокси-пользователем токен

    Прокси-аутентификация — это аутентификация под именем пользователя №2 от имени пользователя №1, именуемого прокси-пользователем.

    Данный вид аутентификации может быть использован внешними планировщиками задач, которые необходимо выполнять под разными пользователями. При этом планировщику нет необходимости хранить и передавать секретные учётные данные пользователей.

Формируется прокси-пользователем и подписывается закрытым RSA-ключом прокси-пользователя.
Открытый RSA-ключ, сопоставленный с учётной записью прокси-пользователя, хранится в БД решения.
Срок годности определяется прокси-пользователем.

Тело JWT (payload) должно содержать следующие поля:

- `typ` — `"ProxyCrt"`.

- `sub` — имя пользователя, под которым необходимо аутентифицироваться.

- `psub` — имя прокси-пользователя, которому принадлежат закрытый и открытый ключи.

- `cid` — идентификатор открытого ключа в системе Global, сопоставленного с прокси-пользователем.

- `exp` — дата устаревания.

При проверке валидности токена на основе полей `psub` и `cid` из БД решения получается открытый RSA-ключ прокси-пользователя и проверяется валидность подписи JWT.
Если токен валиден и пользователь `psub` имеет права на выполнение кода от имени других пользователей, запрос аутентифицируется под именем пользователя `sub`.

### Пример запроса с аутентификацией по токену

В примере используются библиотеки:

- `"io.jsonwebtoken" % "jjwt-api" % "0.10.8"`.

- `"io.jsonwebtoken" % "jjwt-impl" % "0.10.8"`.

- `"io.jsonwebtoken" % "jjwt-jackson" % "0.10.8"`.

- `"org.apache.httpcomponents" % "httpclient" % "4.5.8"`.

    import io.jsonwebtoken.Jwts;
    import org.apache.http.HttpHeaders;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.junit.Assert;
    import org.junit.Test;

    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.nio.charset.StandardCharsets;
    import java.security.KeyPairGenerator;
    import java.security.NoSuchAlgorithmException;
    import java.security.KeyPair;
    import java.security.PrivateKey;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.stream.Collectors;

    public class AuthProviderTest {

        private PrivateKey getPrivateKey(String proxyUser) throws NoSuchAlgorithmException {
            // Формируется рандомный закрытый ключ.
            // В рабочем коде необходимо использовать реальный закрытый ключ, принадлежащий пользователю.
            return KeyPairGenerator.getInstance("RSA").generateKeyPair().getPrivate();
        }

        @Test
        public void authenticate_with_gjwt_proxy_cert() throws Exception {
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DAY_OF_MONTH, 7);
            String proxyUser = "proxy_user";
            String gjwt = "gjwt_" + Jwts.builder()
                    .setAudience("GS")                  // Кому предназначен токен. Необязательный параметр.
                    .setIssuer("Scheduler")             // Кем сформирован токен. Необязательный параметр.
                    .setSubject("real_user")            // Имя пользователя, под которым необходимо аутентифицироваться.
                    .claim("typ", "ProxyCrt")           // Подтип токена. В данном случае, токен для прокси-аутентификации.
                    .claim("psub", proxyUser)           // Имя прокси-пользователя.
                    .claim("cid", "123456789")          // Идентификатор открытого RSA-ключа прокси-пользователя в системе Глобал.
                    .setExpiration(calendar.getTime())  // Дата устаревания токена.
                    .signWith(getPrivateKey(proxyUser)) // Закрытый ключ прокси-пользователя, которым будет подписан токен.
                    .compact();

            HttpGet httpGet = new HttpGet("http://localhost:8080/PGTEST/app/sys/rest/ss/pkg/Gs3_RestfulTestPkg/anypath");
            httpGet.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + gjwt);
            try (CloseableHttpClient client = HttpClients.createDefault()) {
                try (CloseableHttpResponse response = client.execute(httpGet)) {
                    try (InputStream in = response.getEntity().getContent()) {
                        String s = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
                                .lines()
                                .collect(Collectors.joining(System.lineSeparator()));
                        System.out.println(s);
                    }
                }
            }
        }
    }

## Администрирование REST-сервисов

Новое расположение документации.

## Администрирование SOAP-вызовов

Новое расположение документации.