Microsoft Graph 유틸리티 기능

Azure Active Directory 관리 센터에서 프로젝트 생성

MS Office 계정을 가진 사용자를 위해 Azure Active Directory 관리 센터에서 프로젝트를 생성합니다.

Azure Active Directory 관리 센터에서 프로젝트 만들기 단계

다음은 Azure Active Directory 관리 센터에서 프로젝트를 만드는 단계별 튜토리얼입니다.

1. Azure Active Directory로 이동하여 MS Office 자격 증명으로 로그인하십시오.

Azure Active Directory 링크 - https://aad.portal.azure.com/

2. 테넌트에 Azure AD 애플리케이션 만들기.

왼쪽 패널에서 Azure Active Directory 레이블을 클릭하십시오. 그러면 Azure Active Directory 블레이드가 열립니다. 해당 화면에서 App registrations 레이블을 볼 수 있습니다. 이것이 Azure AD 애플리케이션을 등록하는 시작점이며, 이 블레이드에서 Azure AD용 새 애플리케이션을 만들 수 있습니다.

New registration 버튼을 클릭하여 새 애플리케이션을 생성하십시오.

todo:image_alt_text

3. 이제 새로운 애플리케이션 등록 블레이드가 표시됩니다.

  • Name 애플리케이션 이름이 됩니다.
  • Supported account types 이 섹션은 액세스를 제한합니다.

Register 버튼을 클릭하십시오.

todo:image_alt_text

4. 새로 등록된 애플리케이션 블레이드가 표시됩니다.

  • Application (client) ID 애플리케이션 ID.
  • Directory (tenant) ID Azure AD 테넌트 ID.

todo:image_alt_text

5. Microsoft Graph API 권한 허용.

API permissions 레이블을 클릭하십시오.

Azure는 이미 애플리케이션에 User.Read 위임 권한을 부여했습니다. 이 권한을 통해 로그인한 사용자의 정보를 읽을 수 있습니다. 이는 Microsoft Graph API 권한이며, 다른 말로 Scopes라고 부를 수 있습니다.

Microsoft Graph API의 전체 범위 목록 - https://docs.microsoft.com/en-us/graph/permissions-reference.

+ Add a permission 버튼을 클릭하고 Microsoft Graph를 선택하십시오.

Delegated permissions를 클릭하십시오. 이제 Microsoft Graph API에서 사용할 수 있는 권한 목록이 표시됩니다.

필요한 권한을 선택하고 Add permissions 버튼을 클릭하십시오.

Grant admin consent 버튼을 클릭하십시오.

todo:image_alt_text

6. 퍼블릭 클라이언트 흐름 허용.

애플리케이션이 퍼블릭 클라이언트인지 지정합니다. 리디렉션 URI를 사용하지 않는 토큰 부여 흐름을 사용하는 앱에 적합합니다.

todo:image_alt_text

7. 애플리케이션용 키 생성

todo:image_alt_text

도우미 클래스

다음 보조 클래스들이 이 섹션의 코드를 실행하는 데 필요합니다. 이 클래스들은 데모를 단순화하기 위해 제공됩니다.

AzureROPCTokenProvider 클래스

다음의 인스턴스: IGraphClient 클래스는 요청을 구성하고 Microsoft Graph API에 전송하며 응답을 처리합니다. 이 클래스의 새 인스턴스를 만들려면 다음 인스턴스를 제공해야 합니다. ITokenProvider, Microsoft Graph에 대한 요청을 인증할 수 있습니다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * Azure resource owner password credential (ROPC) token provider
 * https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc
 * https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth
 * https://portal.azure.com
 * https://developer.microsoft.com/en-us/graph/graph-explorer/#
 * token parser https://jwt.io
 * </p>
 */
class AzureROPCTokenProvider implements ITokenProvider {

    private static final String GRANT_TYPE = "password";

    private final String clientId;
    private final String clientSecret;
    private final String userName;
    private final String password;
    private final String tenant;
    private final String scope;

    private OAuthToken token;

    public AzureROPCTokenProvider(String tenant, String clientId, String clientSecret,
                                  String userName, String password, String[] scopeAr) {
        this.tenant = tenant;
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.userName = userName;
        this.password = password;
        this.scope = joinToStr(scopeAr, " ");
    }

    public synchronized OAuthToken getAccessToken(boolean ignoreExistingToken) {
        if (this.token != null && !this.token.getExpired() && !ignoreExistingToken)
            return this.token;
        token = null;

        Map<String, String> tokenArgs = geToken();

        java.util.Calendar c = java.util.Calendar.getInstance();
        c.add(java.util.Calendar.SECOND, Integer.parseInt(tokenArgs.get("expires_in")));
        token = new OAuthToken(tokenArgs.get("access_token"), TokenType.AccessToken, c.getTime());
        return token;
    }

    public final OAuthToken getAccessToken() {
        return getAccessToken(false);
    }

    public void dispose() {
    }

    private String getEncodedParameters() {
        return "client_id=" + urlEncode(clientId) + "&scope=" + urlEncode(scope) + "&username=" + urlEncode(userName)
                + "&password=" + urlEncode(password) + "&grant_type="
                + urlEncode(GRANT_TYPE);
    }

    private String getUri() {
        if (tenant == null || tenant.trim().isEmpty())
            return "https://login.microsoftonline.com/common/oauth2/v2.0/token";
        else
            return "https://login.microsoftonline.com/" + tenant + "/oauth2/v2.0/token";
    }

    private Map<String, String> geToken() {
        try {
            HttpURLConnection connection = (HttpURLConnection) new URL(getUri()).openConnection();
            connection.setRequestMethod("POST");

            byte[] requestData = getEncodedParameters().getBytes(StandardCharsets.UTF_8);

            connection.setUseCaches(false);
            connection.setDoInput(true);
            connection.setDoOutput(true);
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.setRequestProperty("Content-Length", "" + requestData.length);

            final OutputStream st = connection.getOutputStream();
            try {
                st.write(requestData, 0, requestData.length);
            } finally {
                st.flush();
                st.close();
            }

            connection.connect();

            if (connection.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
                throw new IllegalAccessError("Operation failed: " + connection.getResponseCode() + "/" +
                        connection.getResponseMessage() + "\r\nDetails:\r\n{2}"
                        + readInputStream(connection.getErrorStream()));
            }

            String responseText = readInputStream(connection.getInputStream());

            Map<String, String> result = new HashMap<>();
            String[] fromJsonToKeyValue = responseText.replace("{", "").replace("}", "")
                    .replace("\"", "").replace("\r", "")
                    .replace("\n", "").split(",");
            for (String keyValue : fromJsonToKeyValue) {
                String[] pair = keyValue.split(":");
                String name = pair[0].trim().toLowerCase();
                String value = urlDecode(pair[1].trim());
                result.put(name, value);
            }

            return result;
        } catch (IOException e) {
            throw new IllegalAccessError(e.getMessage());
        }
    }

    static String urlEncode(String value) {
        try {
            return URLEncoder.encode(value, StandardCharsets.UTF_8.toString());
        } catch (UnsupportedEncodingException e) {
            throw new IllegalAccessError(e.getMessage());
        }
    }

    static String urlDecode(String value) {
        try {
            return URLDecoder.decode(value, StandardCharsets.UTF_8.toString());
        } catch (UnsupportedEncodingException e) {
            throw new IllegalAccessError(e.getMessage());
        }
    }

    static String readInputStream(InputStream is) {
        if (is == null)
            return "";

        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder result = new StringBuilder();
        String line;
        try {
            while ((line = reader.readLine()) != null) {
                result.append(line);
            }
        } catch (IOException e) {
            // ignore
        }
        return result.toString();
    }

    static String joinToStr(String[] arr, String sep) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            if (i > 0)
                sb.append(sep);
            sb.append(arr[i]);
        }
        return sb.toString();
    }
}