Как реализовать простой веб-сервис с доступом на основе токенов с помощью KeyCloak? - PullRequest
0 голосов
/ 07 марта 2019

Я хочу создать веб-сервис, который будет работать следующим образом:

  1. Клиент подключается к KeyCloak и предоставляет имя пользователя и пароль.

  2. KeyCloak возвращает токен доступа клиенту.

  3. Используя этот токен доступа, клиент получает доступ к странице, защищенной KeyCloak.

Какя могу это сделать?

Что я пробовал

Шаг 1: Клиент получает токен доступа

private String getAccessToken() throws IOException {
    final CloseableHttpClient client = HttpClients.createDefault();

    final List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(4);
    nameValuePairs.add(new BasicNameValuePair("client_id", "test-app"));
    nameValuePairs.add(new BasicNameValuePair("username", "user1"));
    nameValuePairs.add(new BasicNameValuePair("password", "user1"));
    nameValuePairs.add(new BasicNameValuePair("grant_type", "password"));


    try {
        final HttpPost post = new HttpPost("http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token");
        post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
        post.setHeader("Accept", "application/json");
        post.setHeader("Content-type", "application/x-www-form-urlencoded");

        final CloseableHttpResponse response = client.execute(post);

        final int statusCode = response.getStatusLine().getStatusCode();

        System.out.println(String.format("Result: %d", statusCode));

        if (statusCode != 200) {
            System.out.println("Something went wrong");
            return null;
        }

        final String responseTxt = EntityUtils.toString(response.getEntity());
        final JSONObject json = new JSONObject(responseTxt);

        final String accessToken = json.getString("access_token");
        return accessToken;
    } finally {
        try {
            client.close();
        } catch (final IOException exception) {
            exception.printStackTrace();
        }
    }
}

Кажется, это работает, когда я получаю код ответа 200 и токен доступа в теле.

Шаг 2. Доступ к защищенному веб-сервису

Веб-сервис работаетв http://127.0.0.1:8090/test.

я использую следующий код для доступа к нему (accessToken - токен доступа из предыдущего шага):

    final String targetUrl = "http://127.0.0.1/test";
    final CloseableHttpClient client = HttpClients.createDefault();
    final HttpGet get = new HttpGet(targetUrl);
    get.setHeader("Authorization", String.format("Bearer%s", accessToken));
    final CloseableHttpResponse response = client.execute(get);
    final int statusCode = response.getStatusLine().getStatusCode();
    System.out.println(String.format("Result: %d", statusCode));
    client.close();

Когда я это делаю, я получаюстраница входа KeyCloak в качестве ответа, а не ответа веб-службы.

Как настроен веб-сервис?

веб-сервис:

@RestController
public class SampleRestController {
    @RequestMapping("/test")
    public String test() {
        return "Hello, world!";
    }
}

Конфигурация безопасности:

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
    @Autowired
    public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {

        KeycloakAuthenticationProvider keycloakAuthenticationProvider
                = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(
                new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }
    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }


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

    @Override
    public void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
                .antMatchers("/test*")
                .hasRole("user")
                .anyRequest()
                .permitAll();
    }
}

application.properties:

server.port = 8090

spring.main.allow-bean-definition-overriding=true


keycloak.realm = myrealm 
keycloak.auth-server-url = http://127.0.0.1:8080/auth
keycloak.ssl-required = external
keycloak.resource = test-app
keycloak.public-client=true
keycloak.security-constraints[0].authRoles[0]=user
keycloak.security-constraints[0].securityCollections[0].patterns[0]=/test/*

Что мне нужно изменить, чтобы получить "Hello", Мир!"Ответ, когда я отправляю запрос с токеном доступа в шапке?

Обновление 1: Я обнаружил одну часть проблемы - неправильная конфигурация клиента.Это должно быть bearer only.

Screenshot

Но теперь я получаю ответ Bearer-only applications are not allowed to initiate browser login.

Обновление2: При следующей конфигурации службы она работает.

server.port = 8090
spring.main.allow-bean-definition-overriding=true
keycloak.realm = myrealm
keycloak.resource = test-app
keycloak.auth-server-url = http://127.0.0.1:8080/auth
keycloak.bearer-only = true
keycloak.ssl-required = external
keycloak.principal-attribute = preferred_username

1 Ответ

0 голосов
/ 07 марта 2019

Вы используете неверный поток или пункты назначения. Если вы хотите использовать логин / пароль, вы можете выбрать 2 из них:

  1. тока. вы пытаетесь получить доступ, ваш сервер проверяет ваш логин, а если нет - перенаправляет на keycloak. После входа в систему keycloak перенаправляет вас на вашу страницу (или страницу входа - я не помню) с кодом доступа (не токеном) . После этого ваш сервис отправляет этот код на keycloak и делает другие вещи

  2. Изменить поток на поток пароля. В этом случае вы отправите имя пользователя / пароль своей службе (не Keycloak), ваша служба отправит эти учетные данные в Keycloak, а после этого сделает еще одну вещь

UPDATE:

Я предпочитаю, чтобы пользователи использовали первые вещи, но если вам нужно войти в систему с помощью другой программы, чем лучше использовать другой механизм. ClientCredentials или пользователь с небольшим набором ролей

UPDATE2:

Если вам действительно нужно использовать токен авторизации, вы можете реализовать пользовательский AccessTokenProvider и настроить свой OAuth2RestTemplate, используя UserInfoRestTemplateCustomizer . Но это не так просто

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...