راه‌اندازی 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 خود خواهید بود.

۱. ایجاد برنامه 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

۲. ایجاد Client Secret

  1. پس از ثبت، به Certificates & SecretsNew Client Secret بروید.
  2. توضیح و دوره انقضا را اضافه کنید.

todo:image_alt_text

  1. مقدار secret تولید‌شده را کپی کنید - دیگر قابل مشاهده نخواهد بود.

client secret را ایمن نگه دارید؛ برای احراز هویت کلاینت محرمانه ضروری است.

باید صفحه برنامه‌های تازه ثبت‌شده را ببینید.

todo:image_alt_text

۳. پیکربندی مجوزهای 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

۴. اجازه دادن به جریان‌های کلاینت عمومی

مشخص کنید آیا برنامه یک کلاینت عمومی است یا نه. مناسب برای برنامه‌هایی که از جریان‌های اعطای توکن استفاده می‌کنند و نیاز به URI هدایت ندارند.

todo:image_alt_text

۵. احراز هویت Microsoft Graph

روش‌های احراز هویت پشتیبانی‌شده در Aspose.Email

| ارائه‌دهنده توکن | موارد استفاده | | ——————————– | ————————————————————————————— | | AzureConfidentialTokenProvider | کلاینت محرمانه (client ID + secret) برای برنامه‌های سمت سرور | | AzureROPCConfiguration | اعتبارنامه‌های مالک منابع (نام کاربری + رمز عبور) برای سناریوهای غیرتعاملی | | AzurePublicTokenProvider | کلاینت عمومی (ورود تعاملی) | | AzureTokenProviderBase | کلاس پایه برای پیاده‌سازی‌های احراز هویت سفارشی |

احراز هویت با استفاده از یک کلاینت محرمانه

از AzureConfidentialTokenProvider برای احراز هویت زمانی که client ID, client secret, and 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 از طریق IGraphClient رابط. برای احراز درخواست‌ها، یک پیاده‌سازی از ITokenProvider ضروری است. در حالی که اکثر توسعه‌دهندگان از ارائه‌دهندگان احراز هویت پیش‌ساخته استفاده می‌کنند، مواردی وجود دارد که ممکن است بخواهید ارائه‌دهنده خود را ایجاد کنید، برای مثال هنگام کار با جریان Resource Owner Password Credentials (ROPC).

۱. پیاده‌سازی 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();
    }
}

۲. ایجاد یک شی 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);
    }
};

۳. استفاده از ارائه‌دهنده توکن سفارشی

زمانی که 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 ارسال کنید.