Проверка аутентификации в прикладном приложении

Java

Аутентификация в прикладных приложениях происходит с помощью JWT-токена в заголовке Authorization. Для проверки и получения данных из JWT-токенов применяются фильтры аутентификации для Spring-Security:
  • plat-jwt-authentication-starter — выполняет проверку JWT-токена и создает объект Authenitcation в прикладном приложении. Этот фильтр используется для эксплуатации в ПРОМ-среде;
  • plat-stumb-jwt-authentication-starter — создает объект Authenitcation и JWT-токены при обращении к прикладному приложению на основе файлов профилей. Этот фильтр используется для локального запуска и для режима отладки на DEV-стендах.
Доступ к стенду и подключение к интернету необходимы для работы как с фильтром для ПРОМ-стенда, так и с фильтром для локального запуска.

Подключение аутентификации в Java-приложениях

При разработке java-приложений рекомендуется подключать оба фильтра аутентификации и переключаться между ними с помощью Spring-профилей.
Для подключения фильтров:
  1. Проверьте наличие подключенного maven-репозитория в .m2/settings.xml:
    settings.xml type=xml
    <servers>
        <server>
            <id>cpr_maven</id>
            <username>ИМЯ УЧЕТНОЙ ЗАПИСИ В SBER CLOUD</username>
            <password>ПАРОЛЬ УЧЕТНОЙ ЗАПИСИ В SBER CLOUD</password>
        </server>
    </servers>
    
    <profiles>
        <profile>
            <id>default</id>
            <repositories>
                <repository>
                    <id>cpr_maven</id>
                    <url>https://Путь_до_зависимостей_в_нексусе</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                </repository>
            </repositories>
        <profile>
    <profiles>
    
  2. Добавьте зависимость с контрактами в pom.xml:
  • для spring-webmvc:
    pom.xml type=xml
       <dependency>
           <groupId>ru.sbrf.ufs.platform</groupId>
           <artifactId>plat-jwt-authentication-starter</artifactId>
           <version>3.1.9</version>
       </dependency>
    
       <dependency>
           <groupId>ru.sbrf.ufs.platform</groupId>ы
           <artifactId>plat-stumb-jwt-authentication-starter</artifactId>
           <version>3.3.1</version>
       </dependency>
    
  • для spring-webflux:
    pom.xml type=xml
       <dependency>
           <groupId>ru.sbrf.ufs.platform</groupId>
           <artifactId>plat-jwt-authentication-starter-webflux</artifactId>
           <version>3.3.1</version>
       </dependency>
    
       <dependency>
           <groupId>ru.sbrf.ufs.platform</groupId>
           <artifactId>plat-stumb-jwt-authentication-starter-webflux</artifactId>
           <version>3.3.1</version>
       </dependency>
    
  1. Добавьте в application.yml настройки для фильтров аутентификации.
    • auth - настройка фильтра plat-jwt-authentication-starter для ПРОМ-среды:
      • urls — массив доверенных URL Keycloak, JWT-токены которых считаются валидными. Токены валидируются по полю ISSUER с проверкой вида «начинается с»;
      • keysLifetime — время жизни публичного ключа проверки токена в локальном хранилище фильтра аутентификации;
      • sslIgnore — флаг пропуска валидации сертификатов SSL при обращении к IAM Keycloak для получения публичных ключей. Эта настройка опциональна, рекомендуется использовать ее только на DEV-стендах.
    • localauth — настройка фильтра plat-stumb-jwt-authentication-starter для локального запуска:
      • profilesDir — путь до папки с профилями. Может быть абсолютным или относительным от корня проекта;
      • defaultProfile — название профиля по умолчанию: название файла (без расширения) в папке с профилями;
      • ufsProviderUrl — URL для генерации JWT-токена и сессии пользователя в UFS Provider. JWT-токен генерируется на основе json-профиля, созданного в прикладном приложении.
    Для ПРОМ-стенда в cреде Platform V Kubernetes:
    application.yml type=yml
    auth:
     urls:
     - <ISSUER address> # адрес ISSUER из JWT-токена
     authorizeUrl: http://platform-gateway/ufs-security # сервис получения permissions (оставить пустым, если у вас в приложении нет проверки на методах @PreAuthorize или @ReactivePermissionCheck)
     sslIgnore: true # если нужно игнорировать https
     useHttpIAM: true # меняет Https на Http при обращении в IAM
     useIAMPort: 7070 # меняет/добавляет порт 7070 при обращении в IAM
     supportMultitenancy: true # значение должно быть true, т.к. platform-gateway и mtp-gateway переписывают мультитенантный эндпойнт
    
    keycloak:
     sslIgnore: true # если нужно игнорировать https
     useHttpIAM: true # меняет Https на Http при обращении в IAM
     useIAMPort: 7070 # меняет/добавляет порт 7070 при обращении в IAM
    
    localauth:
      profilesDir: profiles
      defaultProfile: 2
      ufsProviderUrl: http://platform-gateway/ufs-provider
    
    Для тестирования в локальном окружении ({адрес_ingress} - берется из паспорта стенда):
    application.yml type=yml
    auth:
     urls:
     - <ISSUER address> # адрес ISSUER из JWT-токена
     authorizeUrl: http://{адрес_ingress}/ufs-security # сервис получения permissions (оставить пустым, если у вас в приложении нет проверки на методах @PreAuthorize или @ReactivePermissionCheck)
     sslIgnore: true # если нужно игнорировать https
     supportMultitenancy: true # значение должно быть true, т.к. platform-gateway и mtp-gateway переписывают мультитенантный эндпойнт
    
    keycloak:
     sslIgnore: true # если нужно игнорировать https
    
    localauth:
     profilesDir: profiles
     defaultProfile: 2
     ufsProviderUrl: http://{адрес_ingress}/ufs-provider
    
  2. Создайте класс SecurityConfiguration. Он позволяет настроить уровень доступности методов и включить дополнительные настройки безопасности, например, CSRF, CORS.
    В этом же классе настраивается переключение фильтров аутентификации: чтобы переключиться на локальный фильтр, добавьте Spring-профиль internal.
    В таблице ниже представлены соответствие поддерживаемых минимальные версии spring-boot фильтрам аутентификации:
    Фильтрspring-boot 3.X.Xspring-boot 2.X.X
    plat-jwt-authentication-starter
    3.3.0all
    plat-stumb-jwt-authentication-starter
    3.3.0all
    java
    17all
    SecurityConfig.java type=java
    package ru.sbrf.ufs.platform.security.demo;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import ru.sbrf.ufs.platform.jwt.authentication.filter.JwtSecurityConfigurerAdapter;
    import ru.sbrf.ufs.platform.stumb.jwt.authentication.filter.StumbJwtSecurityConfigurerAdapter;
    import org.springframework.core.env.Environment;
    
    import java.util.Arrays;
    
       @Configuration
       public class SecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        JwtSecurityConfigurerAdapter jwtSecurityConfigurerAdapter;
    
        @Autowired
        StumbJwtSecurityConfigurerAdapter stumbJwtSecurityConfigurerAdapter;
    
        @Autowired
        Environment env;
    
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
         return super.authenticationManagerBean();
        }
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            boolean isLocal = Arrays.asList(env.getActiveProfiles()).contains("internal");
            //@formatter:off
            http
                    .httpBasic().disable()
                    .csrf().disable()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                    .and()
                    .authorizeRequests()
                    .antMatchers("/anonym/**").permitAll()
                    .antMatchers("/anonym").permitAll()
      
                    .anyRequest().authenticated()
                    .and()
                    .apply(isLocal?stumbJwtSecurityConfigurerAdapter:jwtSecurityConfigurerAdapter);
            //@formatter:on
        }
    }
    
    Библиотека позволяет полностью пропускать фильтр аутентификации для запросов, которым она не требуется (например, swagger и healthcheck). Для этого в классе настройщика безопасности нужно определить метод public void configure(WebSecurity web) throws Exception и указать нужные запросы в списке ignoring.
    Пример:
    SecurityConfig.java type=java
    @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/health",
                    "/print-form/**",
                    "/parameters/**",
                    "/dictionary/**",
                    "/user/jwt",
                    "/sup/**",
                    "/masking/**",
                    "/v2/api-docs",
                    "/v3/api-docs",
                    "/v3/api-docs/**",
                    "/configuration/ui",
                    "/swagger-resources/**",
                    "/configuration/security",
                    "/swagger-ui.html",
                    "/swagger-ui/**",
                    "/*.zip",
                    "/webjars/**",
                    "/actuator/**");
        }
    
  3. Для демонстрации данных, полученных после аутентификации, добавьте контроллер UserController.
    Чтобы получить данные аутентификации, воспользуйтесь интерфейсом org.springframework.security.core.Authentication. Параметры:
    • credentials — значение JWT-токена;
    • details — данные из JWT в виде HashMap;
    • principal — значение поля Issuer из JWT-токена.
    UserController.java type=java
    package com.sbt.demo.ufsrest.controllers;
    
    import io.swagger.v3.oas.annotations.Operation;
    import io.swagger.v3.oas.annotations.media.Content;
    import io.swagger.v3.oas.annotations.media.Schema;
    import io.swagger.v3.oas.annotations.responses.ApiResponse;
    import io.swagger.v3.oas.annotations.responses.ApiResponses;
    import io.swagger.v3.oas.annotations.security.SecurityRequirement;
    import io.swagger.v3.oas.annotations.tags.Tag;
    import lombok.RequiredArgsConstructor;
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.security.core.Authentication;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import ru.sbrf.ufs.platform.jwt.authentication.filter.authorization.efs.UserInfoService;
    import ru.sbrf.ufs.provider.api.dto.user.UserInfo;
    
    import java.util.Map;
    
    @Tag(name = "Данные пользователя")
    @RestController
    @RequestMapping("user")
    @RequiredArgsConstructor
    @CrossOrigin(origins = "*")
    public class UserController1 {
    
        private final UserInfoService userInfoService;
    
        @GetMapping("authentication")
        @PreAuthorize("hasAuthority('SYNT_MS.SMEV.View')")
        @Operation(summary = "Отображение прав и групп пользователей",
                security = @SecurityRequirement(name = "bearerAuth"))
        public Authentication authentication(Authentication authentication) {
            return authentication;
        }
    
        @Operation(summary = "Получение данных о пользователе ЕСИА", description="Позволяет получить данные о пользователе ЕСИА, которые были получены на этапе аутентификации." +
                "Состав данных настраивается в IAM."+
                "В запросе необходимо передать два заголовка: Authorization с Bearer JWT",
                security = @SecurityRequirement(name = "bearerAuth"))
        @ApiResponses(value = {
                @ApiResponse(responseCode = "200", description = "Запрос выполнен успешно",
                        content = @Content(schema = @Schema(implementation = UserInfo.class))),
                @ApiResponse(responseCode = "403", description = "Некорректное JWT")
        })
        @GetMapping("personal")
        public Map<String, Object> personal(Authentication authentication) {
            return userInfoService.getFlatUserInfo(authentication);
        }
    }
    

Настройка аудита

Для библиотеки авторизации и регистрации событий информационной безопасности рекомендуется настроить метамодель аудита. Это позволит видеть события, которые автоматически собирает библиотека.
Чтобы настроить аудит, определите параметры в тегах audit и metaModel.
ПараметрыПример заполнения / Тип данныхОписание
Тег audit
enabled
booleanВключение/выключение аудита. По умолчанию false
url
http://audit_url/Хост аудита
mode
ASYNC, SYNCРежим отправки запросов в аудит. При режиме ASYNC запросы обрабатываются в разных потоках без необходимости ждать выполнения предыдущих
Тег metaModel
metamodelVersion
integerВерсия метамодели. Инкрементируйте значение версии при любом изменении модели
module
stringИмя прикладного приложения
events
[{...},...]События, которые нужно аудировать
Модель данных для аудита регистрируется на старте приложения.
Пример настройки аудита:
application.yml type=yml
#Настройка аудита Платформы
audit:
  #Mode of sending messages to audit(default sync). available values [async|sync].
  mode: sync| # синхронно или асинхронно отправлять запросы в  audit-service(по умолчанию sync). Доступные значения [async|sync]
  enabled: false # вкл/выкл
  url: { AUDIT_HOST }
  metamodel:
    metaModelVersion: 1 # инкрементировать каждый раз при изменении модели
    module: { NAME } # имя модуля
    events:
      - name: FAILED_LOGIN
        description: Ошибочная попытка логина
        success: true
        mode: reliability
        params:
          - name: jwt
            description: JWT токен
          - name: reason
            description: Причина
          - name: appHost
            description: Хост, с которого отправили событие в аудит
      - name: UNAUTHORIZED_ACCESS
        description: Ошибочная попытка авторизации
        success: true
        mode: reliability
        params:
          - name: sessionId
            description: Идентификатор сессии
          - name: requiredPermission
            description: Требуемые разрешения
          - name: api
            description: Проверямый эндпоинт
          - name: appHost
            description: Хост, с которого отправили событие в аудит
      - name: AUTHORIZED_SUCCESS
        description: Успешная авторизация
        success: true
        mode: reliability
        params:
          - name: sessionId
            description: Идентификатор сессии
          - name: permission
            description: Требуемые разрешения
          - name: api
            description: Проверямый эндпоинт
          - name: appHost
            description: Хост, с которого отправили событие в аудит
События в блоке events отправляются в аудит с помощью класса ru.sbrf.ufs.platform.audit.AuditFacade.

Общий случай

Если вам требуется получать права и группы пользователей в приложениях на других языках программирования, или вы не можете использовать Spring Security, воспользуйтесь методом /authorize UFS Provider. В ответ на запрос с переданным JWT-токеном метод вернет права и группы пользователя. Подробнее см. в описании API и в разделе «Интеграционные шлюзы».

Демопримеры

В демопримерах реализованы стандартные сценарии, демонстрирующие работу отдельных сервисов Платформы ГосТех. Отправка тестовых запросов в демопримерах позволит вам ознакомиться с функциями того или иного сервиса. Доступные демопримеры и описания реализованной в них функциональности представлены ниже.
Аутентификация
Аутентификация в прикладных приложениях с проверкой JWT токена и присвоением сессии уникального идентификатора
Создание локального JWT токена
Создание JWT токена с настраиваемым набором данных пользователя для аутентификации в прикладных приложениях
Получение данных в ЕСИА
Просмотр данных о пользователе из ЕСИА, полученных при аутентификации
Предыдущий раздел
Интеграция с внешним LDAP-каталогом
Следующий раздел
Просмотр активных сессий пользователя
Была ли страница полезной?