[Перевод] Реализация авторизации на основе ролей в Spring Boot с помощью Keycloak

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

В предыдущей статье мы узнали, как защитить Spring Boot REST API с помощью Keycloak, используя протокол аутентификации OpenID Connect.

В этой статье мы добавим авторизацию на основе ролей.

Цель состоит в том, чтобы разрешить доступ к некоторым конечным точкам только пользователям с определенной ролью. Точнее, мы собираемся ограничить доступ к конечной точке DELETE и сделать его доступным только пользователям с ролью администратора.

Добавление новой конечной точки

Давайте сначала расширим Spring Controller, добавив новую конечную точку DELETE.

package com.mozen.springbootkeycloack.controller;

import com.mozen.springbootkeycloack.model.Plant;
import com.mozen.springbootkeycloack.service.PlantService;
import com.sun.istack.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController()
@RequestMapping("/plant")
public class PlantController {

		...

    @DeleteMapping("/{plantId}")
    public void deletePlant(@PathVariable long plantId) {

        log.info("Delete plant request for plant " + plantId + " received");

        plantService.deletePlant(plantId);
    }
}

Расширение конфигурации

Возвращаясь к файлу application.yml, мы указываем новое свойство для конфигурации Keycloak.

...

keycloak:
	...
  use-resource-role-mappings: false

Устанавливаем свойству use-resource-role-mappings значение false.

Это означает, что авторизация будет основана на ролях уровня Keycloak realm, а не на клиентских ролях, специфичных для приложения Spring Boot.

Мы делаем это потому, что используемая нами по умолчанию роль администратора Keycloak определена как роль уровня realm.

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

Также необходимо внести небольшие изменения в WebSecurityConfiguration.

@KeycloakConfiguration
public class WebSecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider =
                keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new NullAuthenticatedSessionStrategy();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.csrf()
                .disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.DELETE,"/plant/**")
                .hasRole("admin")
                .anyRequest()
                .authenticated()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}

Мы добавляем новый antMatcher, который ограничивает все маршруты, начинающиеся с '/plant/' и использующие метод HTTP DELETE, что соответствует конечной точке deletePlant, которую мы добавили ранее.

Обратите внимание, что сопоставление ролей осуществляется с помощью SimpleAuthorityMapper. По умолчанию Spring Security добавляет префикс 'ROLE_' к любой должности, но роли Keycloak этого не делают.

При использовании этого сопоставителя префикс будет добавлен к любой должности, отправленной в токене Keycloak, если его еще нет.

Конфигурация Keycloak

У нас уже есть пользователь 'admin' из предыдущей статьи. Этот пользователь уже имеет роль 'admin'.

Нам нужно создать нового пользователя, не имеющего роли администратора

Он будет использоваться для демонстрации того, что авторизация на основе ролей работает и что конечная точка DELETE будет запрещена для этого пользователя.

Тестирование приложения

Для начала запустим приложение.

mvn spring-boot:run

Чтобы протестировать установку, мы будем использовать тот же метод, что и в предыдущей статье, а также Postman в роли клиента.

Мы улучшим конфигурацию Postman, добавив нового пользователя в коллекцию переменных

Давайте сначала убедимся, что «user» без роли администратора не может получить доступ к конечной точке delete.

Сначала мы получим токен с помощью пользователя без роли администратора.

И давайте попробуем использовать конечную точку delete, предоставив этот токен в заголовке авторизации.

Как и ожидалось, мы получаем ошибку 401 Unauthorized, поскольку роль администратора отсутствует.

На этот раз мы получаем обратно новый токен с пользователем admin.

Теперь конечную точку delete можно успешно использовать.

На этом все! Теперь у нас есть авторизация на основе ролей.

Обратите внимание, что начиная с версии Spring Security 5.7.0, WebSecurityConfigurerAdapter устарел. Теперь рекомендуется использовать новый тип конфигурации в соответствии с компонентно-ориентированным дизайном. В настоящее время Keycloak Spring Boot Adapter не поддерживает этот новый тип конфигурации. Я постараюсь обновить эту статью для поддержки этого нового типа конфигурации как можно скорее.

Вы можете получить доступ к демо-проекту для этой статьи на github.

Скоро состоится открытое занятие «События в Spring Data JPA». На этой встрече затронем такую важную тему, как работа с событиями, генерируемыми при взаимодействии с JPA сущностями. Записаться можно на странице курса «Разработчик на Spring Framework».

© Habrahabr.ru