Аутентификация

Аутентификация — это первая часть взаимодействия с REST API. Она необходима для того, чтобы Jmix мог идентифицировать пользователя, выполняющего запрос. Это позволяет Jmix накладывать ограничения разрешений/доступа к данным на запросы, чтобы гарантировать, что пользователю мог видеть/взаимодействовать только с разрешенным ему частями системы.

Запрос аутентификации

Для взаимодействия с Jmix REST API требуется аутентификация через поток OAuth2. Краткий обзор представлен на следующей диаграмме последовательности:

authentication diagram

Во-первых, клиент API должен запросить токен доступа через запрос аутентификации. Для этого нужно отправить запрос www-form-urlencoded с именем пользователя и паролем как часть тела формы в Jmix:

Authentication Request
POST http://localhost:8080/oauth/token
Authorization: Basic {{client_id}} {{client_secret}} (1)
Content-Type: application/x-www-form-urlencoded (2)

grant_type=password (3)
&username={{username}} (4)
&password={{password}}
1 Сам Authentication Request аутентифицируется с помощью Basic Auth client_id и client_secret.
2 Содержимое запроса аутентификации – application/x-www-form-urlencoded.
3 Значение grant_typepassword, чтобы обозначить тип логина.
4 Учетные данные пользователя предоставляются как часть формы с ключами username и password.

После успешного входа в систему Jmix возвращает токен доступа как часть ответа HTTP:

HTTP/1.1 200
{
  "access_token": "CXE0w/9cOsnpSo8v2jEDoI8Qa3Y=",
  "token_type": "bearer",
  "refresh_token": "Hh2xCuZ7fgd35obagEBNGevF4ws=",
  "expires_in": 31535999,
  "scope": "rest-api",
  "OAuth2.SESSION_ID": "5C46CDF266E8C8C15372887830B74F59"
}

Атрибут access_token содержит токен, который вы можете использовать в дальнейших запросах: CXE0w/9cOsnpSo8v2jEDoI8Qa3Y=.

Обновление токена

Токен действует как временные учетные данные/пароль. По истечении определенного периода срок действия токена истекает, и становится необходимо запросить новый. Первый вариант – снова запросить у пользователя его учетные данные и выполнить обычный запрос аутентификации.

Не храните имя пользователя и пароль в своем клиентском приложении, чтобы пользователю не нужно было повторно вводить свои учетные данные. Либо снова запросите у пользователя его учетные данные, либо используйте токен обновления, чтобы предотвратить истечение срока действия.

Исходный Authentication Request содержит refresh_token, который можно использовать для создания нового токена доступа. В то время как токен доступа сравнительно недолговечен (по умолчанию 12 часов для Jmix), токен обновления по умолчанию действителен в течение 365 дней. Поэтому он хорошо подходит в качестве долгоживущего способа для выпуска новых токенов.

Чтобы получить новый токен доступа с помощью токена обновления, вы можете использовать ту же операцию. Но вместо использования grand_type: password вы также можете использовать refresh_token. Пример использования токена обновления:

Refresh Authentication Request
POST http://localhost:8080/oauth/token
Authorization: Basic {{client_id}} {{client_secret}}
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token (1)
&refresh_token=Hh2xCuZ7fgd35obagEBNGevF4ws= (2)
1 Значение grant_typerefresh_token, чтобы обозначить тип логина.
2 Токен обновления предоставляется как часть формы с ключом refresh_token.

Ответ содержит новый токен доступа с новым сроком действия. При этом вы можете сохранить доступ к API без необходимости запрашивать у пользователя его учетные данные после истечения срока действия токена доступа.

Выполнение запросов к API

Существует два способа использования токена доступа для аутентификации в различных API-интерфейсах Jmix, в зависимости от варианта использования. Начнем с первого и основного: через HTTP-заголовок в следующих запросах.

Аутентификация через заголовок

С полученным токеном доступа CXE0w/9cOsnpSo8v2jEDoI8Qa3Y= вы можете выполнять запросы к REST API. Токен необходимо передавать в качестве заголовка (Header) Authorization в каждом запросе, как в следующем примере:

Create Customers Request
POST http://localhost:8080/rest
            /entities
            /rstex11_Customer
Authorization: Bearer CXE0w/9cOsnpSo8v2jEDoI8Qa3Y=  (1)

{
  name: "Randall Bishop"
}
1 Запрос API аутентифицируется с помощью типа авторизации Bearer и полученного токена доступа.

Аутентификация через параметр URL

Также можно передать токен доступа в качестве параметра запроса URL. Это необходимо, когда установить заголовок HTTP невозможно в случае ссылки браузера на файл или отображения изображения.

В следующем примере изображение из Files API должно отображаться на сайте через <img src="…​" />.

В этом случае установить заголовок HTTP невозможно. Передадим access_token как параметр запроса URL:

<img
    src="http://localhost:8080/files
            ?access_token=CXE0w/9cOsnpSo8v2jEDoI8Qa3Y=
            &fileRef=fs://2021/03/12/a3b6011d-9040-151e-7d17-f7ccdf75d72f.jpg?name=cat.jpg"
/>

Анонимный доступ

По умолчанию все операции доступны только после успешной аутентификации в приложении. Но также возможно предоставить определенные части REST API и без аутентификации с помощью функции анонимного доступа Jmix. В этом случае запрос API выполняется от имени пользователя anonymous, который настроен по умолчанию в приложении Jmix.

Для каждой защищенной операции, которая вызывается без заголовка Authentication, пользователь будет аутентифицирован как anonymous.

Чтобы внести определенные операции в белый список для анонимного доступа, задайте список шаблонов URL-адресов, разделенных запятыми, в свойстве приложения jmix.rest.anonymous-url-patterns. Например:

jmix.rest.anonymous-url-patterns = \
  /rest/services/productService/getProductInformation,\
  /rest/entities/Product,\
  /rest/entities/Product/*

Последний шаблон в приведенном выше примере необходим, если вы хотите обновить или удалить объект Product, потому что в этом случае URL-адрес имеет часть идентификатора.

Как только параметр установлен, можно взаимодействовать с ProductService без отправки заголовка Authorization:

GetProductInformation Request
GET {{baseRestUrl}}
         /services
         /productService
         /getProductInformation
         ?productId=123
# Authorization: not set

На этот запрос вернется успешный ответ сервиса:

HTTP/1.1 200
{
  "name": "Apple iPhone",
  "productId": "123",
  "price": 499.99
}

Если вы хотите предоставить анонимный доступ к операциям некоторых сущностей, убедитесь, что у пользователя anonymous есть права на эти сущности. Для этого создайте ресурсную роль и назначьте ее пользователю anonymous в методе DatabaseUserRepository.initAnonymousUser(). Например:

@ResourceRole(name = "AnonymousRestRole", code = AnonymousRestRole.CODE, scope = "API")
public interface AnonymousRestRole {

    String CODE = "anonymous-rest-role";

    @EntityAttributePolicy(entityClass = Product.class,
        attributes = "*",
        action = EntityAttributePolicyAction.MODIFY)
    @EntityPolicy(entityClass = Product.class,
        actions = {EntityPolicyAction.READ, EntityPolicyAction.UPDATE})
    void product();
}
@Primary
@Component("UserRepository")
public class DatabaseUserRepository extends AbstractDatabaseUserRepository<User> {
    // ...

    @Override
    protected void initAnonymousUser(User anonymousUser) {
        Collection<GrantedAuthority> authorities = getGrantedAuthoritiesBuilder()
                .addResourceRole(AnonymousRestRole.CODE)
                .build();
        anonymousUser.setAuthorities(authorities);
    }
}
Функция анонимного доступа не требует наличия роли rest-minimal у пользователя anonymous.