May 22, 2019

FAQ: работа с IDCS через REST API из Java

Short intro: here you can find an example - how to work with IDCS (receive oauth token, query for users and group) from Java using Jersey lib

Пришлось разобраться с тем, как работать с Identity Cloud Service REST API из Java. Несмотря на то, что IDCS REST API хорошо документирован, а работа с API хорошо описана в соответствующих обучающих материалах и имеется коллекция REST запросов для Postman, мне пришлось немного поработать, чтобы "подружить" IDCS REST API с Java при помощи библиотек Jersey для вызова REST сервисов. Под катом - пример Java программы с комментариями.




Для доступа к IDCS мы зарегистрируем приложение. Как это сделать, подробно рассказано в IDCS Workshop.  Нужно зайти в IDCS Admin Console, перейти в Applications, нажать Add для добавления нового приложения, выбрать Confidential Application. URL можно не заполнять, а на шаге Client нужно убедиться, что отмечена «галочка» Client Credentials (программа для соединения будет использовать ClientID и Client Secret). 



Также укажите доступные приложению внутри IDCS полномочия (я выделил все, но в-целом можно выделить лишь некоторые).


В итоге вы должны получить значение ClientID и Client Secret, наподобие показанного ниже. Эти значения будут использоваться для аутентификации нашей программы в IDCS.



Таким образом, вы создали приложение в IDCS, из-под которого будет выполнять соединение ваша программа. Теперь нам нужно написать Java код, который будет соответствующим образом вызывать REST сервисы IDCS. Существуют разные способы вызова REST API из Java, я буду использовать библиотеки Jersey (https://jersey.github.io/). Для начала вам необходимо скачать библиотеки отсюда (https://jersey.github.io/download.html) и включить их в CLASSPATH



Дальше разберем Java код. Следующий код используется для получения токена аутентификации.Токен по умолчанию действителен один час. Вам потребуется заменить IDCS URL и ваши значения ClientID и Client Secret, указанные в переменной authcred в формате ClientID:ClientSecret. Это значение потом кодируется в Base64 и подставляется как Basic аутентификация, запрос - HTTP POST.

        Encoder encoder = Base64.getEncoder();
        String authcred = "5a01c43d6XXXXXXX80bb0cf64d667b3e:3e8eb730-94XXXXXXX39d895b63758";
        String authcred64 = encoder.encodeToString(authcred.getBytes());
        System.out.println("auth cred: " + authcred64);

        // authorization, get bearer
        MultivaluedMap formData = new MultivaluedHashMap();
        formData.add("grant_type", "client_credentials");
        formData.add("scope", "urn:opc:idm:__myscopes__");
          
        Client client = ClientBuilder.newClient();
        WebTarget webTarget = client.target("https://idcs-fa04eXXXXXXXe5f5c007167.identity.oraclecloud.com");
        Response resp = webTarget.path("oauth2/v1/token").request().header("Content-Type", "application/x-www-form-urlencoded")
                                                         .header("X-RESOURCE-IDENTITY-DOMAIN-NAME", "cissandbox")
                                                         .header("Authorization", "Basic " + authcred64)
                                                         .post(Entity.form(formData));
        
        System.out.println("Resp: " + resp.toString() + "\n");

Отладочный вывод покажет, как именно выглядит токен аутентификации (access_token).


Далее вытащим атрибуты из полученного ответа:

       HashMap respData = resp.readEntity(HashMap.class);
        String accessToken = (String)respData.get("access_token");
        String tokenType = (String)respData.get("token_type");
        BigDecimal expiresIn = (BigDecimal)respData.get("expires_in");
        System.out.println("access_token = " + accessToken);
        System.out.println("token_type = " + tokenType);
        System.out.println("expires_in = " + expiresIn);
        resp.close();

Теперь полученный токен можно использовать при вызове REST API IDCS. Например, выведем всех зарегистрированных пользователей, указав "Bearer + access_token" в заголовке Authorization.

        // USER operations - get ALL users
        resp = webTarget.path("admin/v1/Users").
            request().
            header("Authorization", "Bearer " + accessToken).
            get();
        System.out.println("Resp: " + resp.toString() + "\n" + resp.readEntity(String.class));
        resp.close();

Отладочный вывод:


Просто? Интереснее (пришлось поработать) заставить работать фильтр поиска, синтаксис которого отличается от описанного в документации (в Query нельзя вставлять "+") и необхдимо использовать UriComponent.encode с соответствующими параметрами для корректной кодировки параметра запроса HTTP GET.

        // USER operations - search
        String filter = "userName eq \"oleg.faynitskiy@oracle.com\"";
        System.out.println("filter: " + filter);
        
        resp = webTarget.path("admin/v1/Users").
            queryParam("filter", UriComponent.encode(filter, UriComponent.Type.QUERY_PARAM_SPACE_ENCODED)).
            request().
            header("Authorization", "Bearer " + accessToken).
            get();
        System.out.println("Resp: " + resp.toString());
        respData = resp.readEntity(HashMap.class);
        BigDecimal totalResults = (BigDecimal)respData.get("totalResults");
        System.out.println("Totalresults: " + totalResults);
        if(totalResults.intValue() > 0) {
            System.out.println(totalResults + " user(s) found");
        } else {
            throw new Exception("No users found with filter " + filter);
        }
        
        // at least one user found
        ArrayList users = (ArrayList)respData.get("Resources");
        HashMap user = (HashMap)users.get(0);
        System.out.println("Processing user: " + user.get("userName"));
        
        Iterator it = user.keySet().iterator(); 
        while(it.hasNext()) {
            String key = (String)it.next();
            System.out.println("user." + key + " = " + user.get(key));
        }
        
        ArrayList emails = (ArrayList)user.get("emails");
        HashMap email = (HashMap)emails.get(0);
        System.out.println("\nuser email: " + email.get("value"));
        resp.close();

Отладочный вывод:


Целиком исходник Java класса, где помимо чтения и поиска пользователей показано чтение и поиск групп, членства в группах, вы можете получить тут.

Напоследок замечу, что большая часть IDCS REST API реализует стандарт SCIM (http://www.simplecloud.info/), это верно для используемых в данном примере endpoint'ов  /Users и /Groups.



No comments:

Post a Comment