Настройка Azure AD и аутентификация Microsoft Graph

Aspose.Email for Java предоставляет полную интеграцию с Microsoft Graph, позволяя разработчикам управлять сообщениями, контактами, календарями и задачами из учетных записей Microsoft 365. Это руководство проведет вас через процесс создания приложения Azure AD и настройки аутентификации для начала разработки с Aspose.Email GraphClient.

Прежде чем использовать API Microsoft Graph с Aspose.Email, необходимо зарегистрировать приложение в Azure Active Directory (Azure AD) и настроить аутентификацию. На этой странице рассматривается:

  • Создание приложения Azure AD (проект).

  • Назначение необходимых разрешений Microsoft Graph.

  • Генерация учетных данных (client ID, client secret, tenant ID).

  • Аутентификация в Java с использованием поставщиков токенов Aspose.Email.

После завершения вы будете готовы взаимодействовать с Microsoft Graph из вашего Java‑приложения.

1. Создать приложение Azure AD

Следуйте этим шагам, чтобы зарегистрировать ваше приложение в портале Azure:

  1. Войдите в Портал Azure.
  2. Перейдите к Azure Active DirectoryApp RegistrationsNew Registration.

todo:image_alt_text

  1. Введите Name для вашего приложения (например, AsposeEmailGraphApp).
  2. Выберите поддерживаемые типы учетных записей:
    • Одноарендный (если только ваша организация будет использовать его)
    • Мультиарендный (если доступ необходим нескольким организациям)
  3. При необходимости задайте Redirect URI (необходимо для интерактивной или веб‑аутентификации).
  4. Нажмите Register.

todo:image_alt_text

2. Создать клиентский секрет

  1. После регистрации перейдите к Certificates & SecretsNew Client Secret.
  2. Добавьте описание и период истечения.

todo:image_alt_text

  1. Скопируйте сгенерированное значение секрета — вы больше не увидите его.

Храните client secret в безопасности; он необходим для аутентификации конфиденциального клиента.

Вы должны увидеть панель недавно зарегистрированных приложений.

todo:image_alt_text

3. Настройка разрешений Microsoft Graph

  1. Перейдите к API PermissionsAdd a PermissionMicrosoft Graph.
  2. Выберите тип разрешений: Delegated или Application, в зависимости от вашего сценария.
  3. Добавьте разрешения, необходимые для операций Aspose.Email:
    • Contacts.ReadWrite – для управления контактами
    • Calendars.ReadWrite – для управления календарями
    • Mail.ReadWrite – для чтения и отправки сообщений
    • Tasks.ReadWrite – для управления задачами
  4. Нажмите Grant Admin Consent, если необходимо.

todo:image_alt_text

4. Разрешить потоки публичного клиента

Укажите, является ли приложение публичным клиентом. Подходит для приложений, использующих потоки выдачи токенов без URI перенаправления.

todo:image_alt_text

5. Аутентификация Microsoft Graph

Поддерживаемые методы аутентификации в Aspose.Email

| Поставщик токена | Сценарий использования | | ——————————– | ————————————————————————————— | | AzureConfidentialTokenProvider | Конфиденциальный клиент (client ID + secret) для серверных приложений | | AzureROPCConfiguration | Учётные данные владельца ресурса (имя пользователя + пароль) для неинтерактивных сценариев | | AzurePublicTokenProvider | Публичный клиент (интерактивный вход) | | AzureTokenProviderBase | Базовый класс для пользовательских реализаций аутентификации |

Аутентификация с использованием конфиденциального клиента

Используйте AzureConfidentialTokenProvider для аутентификации, когда у вас есть client ID, client secret и tenant ID:

AzureConfidentialTokenProvider provider = new AzureConfidentialTokenProvider(
    tenantId,
    clientId,
    clientSecret
);

IGraphClient client = GraphClient.getClient(provider, tenantId);
client.setResource(ResourceType.Users);
client.setResourceId(username);
client.setEndpoint("https://graph.microsoft.com");

Это настраивает полностью аутентифицированный IGraphClient, готовый взаимодействовать с Microsoft Graph.

Аутентификация с использованием ROPC (Имя пользователя и пароль)

Для сценариев, где у вас есть имя пользователя и пароль, используйте AzureROPCConfiguration:

AzureROPCConfiguration ropcConfig = new AzureROPCConfiguration(
    tenantId,
    clientId,
    clientSecret,
    username,
    password
);

IGraphClient client = GraphClient.getClient(ropcConfig, tenantId);
client.setResource(ResourceType.Users);
client.setResourceId(username);
client.setEndpoint("https://graph.microsoft.com");

Пользовательские поставщики токенов для Microsoft Graph

Aspose.Email for Java интегрируется с Microsoft Graph через IGraphClient интерфейса. Чтобы аутентифицировать запросы, нужна реализация ITokenProvider необходимо. Хотя большинство разработчиков используют встроенные провайдеры аутентификации, существуют сценарии, когда вы можете захотеть создать собственный провайдер, например, при работе с потоком Resource Owner Password Credentials (ROPC).

1. Реализовать ITokenProvider с использованием AzureROPCTokenProvider

Этот класс предоставляет реализацию ITokenProvider с использованием потока Azure Resource Owner Password Credentials (ROPC). Следующий пример предназначен только для демонстрации. В продакшене мы рекомендуем использовать более безопасные потоки, такие как учетные данные клиента или код авторизации с PKCE.

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 = getToken();

        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> getToken() {
        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();
    }
}

2. Создать объект ITokenProvider

Этот IGraphClient интерфейс отвечает за построение запросов, их отправку в Microsoft Graph и обработку ответов. Чтобы создать экземпляр IGraphClient, вы должны предоставить реализацию ITokenProvider. Поставщик токенов аутентифицирует запросы, предоставляя действительный токен доступа OAuth.

Следующий пример кода демонстрирует, как создать базовую встроенную реализацию ITokenProvider интерфейса, который требуется для аутентификации запросов Microsoft Graph:

ITokenProvider tokenProvider = new ITokenProvider() {
    Date expirationDate = null;

    @Override
    public void dispose() {
        // Clean up resources if necessary
    }

    @Override
    public OAuthToken getAccessToken(boolean ignoreExistingToken) {
        // Retrieve an OAuth access token.
        // If ignoreExistingToken is true, always request a new token.
        // Otherwise, return the existing token if it is valid, or request a new one.
        return null;
    }

    @Override
    public OAuthToken getAccessToken() {
        // Return a valid OAuth token.
        // If no valid token exists, request a new one.
        return new OAuthToken("token", expirationDate);
    }
};

3. Использование пользовательского поставщика токенов

После того как ITokenProvider настроен, вы можете создать GraphClient экземпляр. Этот клиент будет использовать предоставленный поставщик токенов для аутентификации при вызове Microsoft Graph.

ITokenProvider provider = new AzureROPCTokenProvider(
        tenantId,
        clientId,
        clientSecret,
        userName,
        password,
        new String[] {"https://graph.microsoft.com/.default"}
);

IGraphClient client = GraphClient.getClient(provider, tenantId);
client.setResource(ResourceType.Users);
client.setResourceId(userName);
client.setEndpoint("https://graph.microsoft.com");

// Now you can call Microsoft Graph APIs
var folders = client.listFolders(null);
for (GraphFolderInfo folder : folders) {
    System.out.println(folder.getDisplayName());
}

После создания и аутентификации клиента вы можете начинать выполнять запросы к сервисам Microsoft Graph.