Рубрики
Без рубрики

Spring boot + Keycloak – защита ваших Api-интерфейсов (часть 2)

В первой части учебника был включен базовую защиту по нашим Api. В этой второй части… С тегами java, spring boot, keycloak.

Первая часть учебник была включена базовую защиту по нашим Api. В этой второй части мы будем добавлять правила авторизации с помощью фао от содержащихся в токеном JWT. Эм примейру лугар, вамос альтерар носсо приложение.yaml e mudar a propriedade e o конечная точка делает использование ключа. Теперь это будет так:

spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:8080/auth/realms/security-api/protocol/openid-connect/certs

Конечная точка Lembrando que nesse безопасность-api é o ном до носсо царство криадо без ключа.

Vamos para crear una classe que vai ser responsavel parex trairas власти делают токен JWT criado palo с ключом.

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Classe responsavel por converter o token gerado pelo Keycloak
 * de maneira a atribuir os authorities do jeito que o spring security
 * utiliza.
 */
@Component
@RequiredArgsConstructor
public class KeycloakJwtAuthenticationConverter implements Converter {

    private final JwtGrantedAuthoritiesConverter defaultGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
    private final ObjectMapper objectMapper;

    @Override
    public AbstractAuthenticationToken convert(Jwt jwt) {
        Collection authorities = Stream
                .concat(defaultGrantedAuthoritiesConverter.convert(jwt).stream()
                        , extractAuthorities(jwt).stream())
                .collect(Collectors.toSet());
        return new JwtAuthenticationToken(jwt, authorities);
    }

    /**
     * orquestra a extração dos authorities
     * @param jwt
     * @return Collection contendo as roles e scopes do jwt
     */
    private Collection extractAuthorities(Jwt jwt) {
        Set rolesWithPrefix = new HashSet<>();
        rolesWithPrefix.addAll(getRealmRoles(jwt));
        rolesWithPrefix.addAll(getResourceRoles(jwt));
        return AuthorityUtils.createAuthorityList(rolesWithPrefix.toArray(new String[0]));
    }

    /**
     * Busca as roles de acesso ao realm
     * @param jwt
     * @return uma colection contendo as roles
     */
    private Set getRealmRoles(Jwt jwt) {
        Set rolesWithPrefix = new HashSet<>();
        JsonNode json = objectMapper.convertValue(jwt.getClaim("realm_access"), JsonNode.class);
        json.elements().forEachRemaining(
                e -> e.elements().forEachRemaining(r -> rolesWithPrefix.add(createRole(r.asText()))));
        return rolesWithPrefix;
    }

    /**
     * Busca as roles de acesso a determinado resource
     * @param jwt
     * @return uma colection contendo as roles
     */
    private Set getResourceRoles(Jwt jwt) {
        Set rolesWithPrefix = new HashSet<>();
        Map map = objectMapper.convertValue(jwt.getClaim("resource_access"), new TypeReference>(){});
        for (Map.Entry jsonNode : map.entrySet()) {
            jsonNode
                    .getValue()
                    .elements()
                    .forEachRemaining(e -> e
                            .elements()
                            .forEachRemaining(r -> rolesWithPrefix.add(createRole(jsonNode.getKey(), r.asText()))));
        }
        return rolesWithPrefix;
    }

    private String createRole(String... values) {
        StringBuilder role = new StringBuilder("ROLE");
        for (String value : values) {
            role.append("_").append(value.toUpperCase());
        }
        return role.toString();
    }
}

Добавил, чтобы каждая role префикс ROLE_ и роли конкретного resource префикса включает в себя также имя resource. Теперь давайте создадим класс для конфигурации spring security.

import lombok.RequiredArgsConstructor;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {

    private final KeycloakJwtAuthenticationConverter keycloakJwtAuthenticationConverter;

    @Override
    public void configure(final HttpSecurity http) throws Exception {
        http
                .authorizeRequests(authz -> authz.antMatchers("/security/**").authenticated())
                .oauth2ResourceServer()
                .jwt().jwtAuthenticationConverter(keycloakJwtAuthenticationConverter);
    }
}

Здесь, кроме того, обеспечить безопасность, включено также @PreAuthorize и @PostAuthorize . Эти книжки получают выражения, написанные на SpEL (Spring Expression Language), и работает проверка на маркер, если клиент запрос имеет разрешения на доступ тот метод или получить доступ к тому, что произошло после выполнения метода, таких как названия аннотации предлагают. Вы можете узнать больше о том, expression-based access control в документации spring этом ссылки В конфигурации spring security, я сообщил, что только endpoint-ы запускаются /security должны быть проверены и setei класс, который мы создали выше, как преобразовать токен JWT.

Чтоб проверить, мы будем создавать конечные точки, необходимые:

import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "/security")
public class SecurityResource {

    /**
     * endpoint sem nenhuma validação de role
     */
    @GetMapping
    public ResponseEntity isAuthenticated() {
        return ResponseEntity.ok().build();
    }

    /**
     * endpoint onde o usuario tem que ter a role user
     */
    @GetMapping(value = "/has-role")
    @PreAuthorize("hasAnyAuthority('ROLE_USER')")
    public ResponseEntity isUser() {
        return ResponseEntity.ok().build();
    }

    /**
     * endpoint onde o usuario tem que ter a role admin
     */
    @GetMapping(value = "/is-admin")
    @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')")
    public ResponseEntity isAdmin() {
        return ResponseEntity.ok().build();
    }

}

В нашем проекте это все готово. Любой конечной точки, который начинается с security требуется токена JWT, созданный нашей Keycloak, и мы смогли проверить фао, содержащиеся на данном JWT с помощью аннотации spring.

Сейчас в Keycloak мы должны создать roles некоторые пользователи для тестирования.

Создание roles:

Создание пользователей:

Создание пользователя:

После того, как создан, в закладке” Credentials добавьте пароль, измените Temporary пра-ОФФ, и нажмите кнопку Set Password для создания пароля пользователя.

Теперь давайте setar a role пользователю. Создаем 2 roles (admin и user) и создаем 3 пользователей (администратора и пользователя). Мы setar a role согласно созданный пользователем. На аба Сопоставления ролей выберите роль без квадро Доступные роли Электронная группа кнопку Add selected под рамку:

Готово, давайте проверим. В почтальон, и в тот же request предыдущем уроке, мы будем менять формат маркер, который мы приказать. Pra – это альтернативный ключ grant_type пункт пароль . Кроме того, добавьте keys username и password с их значениями. В этом первом примере я искал маркер с пользователь пользователь команда eesti usuario выполняет роль пользователь . Nos конечные точки, требующие решения об обычной роли, которую играет терия, доступная для ао конечная точка /безопасность/имеет роль мас нао ао /безопасность/is-администратор . Идеальный… работает! Уже маркера пользователя admin что есть role admin доступ к /безопасность/is-администратор функция:

Хорошо, это он! Мы настроили Keycloak, чтобы позволить нам создавать токены, проверка подлинности пользователей и назначение им определенные права. Настроим наш проект spring для проверки этого токена и использовать данные, содержащиеся в JWT, чтобы дать авторизации пользователей в нашем приложении.

Первая часть этого учебника, это здесь и код, используемый, например, это в моем github

Большое объятие!

Оригинал: “https://dev.to/mmacorin/spring-boot-keycloak-protegendo-suas-apis-parte-2-5bb4”