Контроль доступа на основе ролей является обязательным условием для любого приложения, имеющего дело с пользователями, которые могут получать доступ к ресурсам в зависимости от своей организационной роли.
В предыдущей статье мы узнали, как защитить наш 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, а не на клиентских ролях, специфичных для приложения Spring Boot.
Мы делаем это потому, что используемая нами по умолчанию роль администратора Keycloak определена как роль уровня царства.
По умолчанию это свойство уже имеет значение 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, добавив нового пользователя в коллекцию переменных
Давайте сначала убедимся, что «пользователь» без роли администратора не может получить доступ к конечной точке удаления.
Сначала мы получим токен с помощью пользователя без роли администратора.
И попробуем воспользоваться конечной точкой удаления, предоставив этот токен в заголовке авторизации.
Как и ожидалось, мы получаем ошибку 401 Unauthorized, поскольку роль администратора отсутствует.
На этот раз мы получаем обратно новый токен с пользователем admin.
Теперь конечная точка удаления может быть успешно использована.
Вот и все! Теперь у нас есть ролевая авторизация.
Обратите внимание, что начиная с версии 5.7.0 Spring Security, WebSecurityConfigurerAdapter устарел. Теперь рекомендуется использовать новый тип конфигурации, следуя компонентно-ориентированному дизайну. На данный момент Keycloak Spring Boot Adapter не поддерживает этот новый тип конфигурации. Я постараюсь обновить эту статью для поддержки этого нового типа конфигурации как можно скорее.
Вы можете получить доступ к демонстрационному проекту для этой статьи блога здесь https://github.com/Mozenn/spring-boot-keycloak-roles.