إعداد Azure AD ومصادقة Microsoft Graph

Aspose.Email for Java توفر تكاملًا كاملاً مع Microsoft Graph، مما يتيح للمطورين إدارة الرسائل، جهات الاتصال، التقويمات، والمهام من حسابات Microsoft 365. يشرح هذا الدليل كيفية إنشاء تطبيق Azure AD وتكوين المصادقة لبدء الترميز باستخدام Aspose.Email GraphClient.

قبل استخدام واجهات Microsoft Graph API مع Aspose.Email، تحتاج إلى تسجيل تطبيق في Azure Active Directory (Azure AD) وتكوين المصادقة. تغطي هذه الصفحة:

  • إنشاء تطبيق Azure AD (مشروع).

  • تعيين أذونات Microsoft Graph الضرورية.

  • إنشاء بيانات الاعتماد (معرف العميل، سر العميل، معرف المستأجر).

  • المصادقة في Java باستخدام موفري الرموز لـ Aspose.Email.

بمجرد الانتهاء، ستكون جاهزًا للتفاعل مع Microsoft Graph من تطبيق Java الخاص بك.

1. إنشاء تطبيق Azure AD

اتبع الخطوات التالية لتسجيل تطبيقك في بوابة Azure:

  1. سجّل الدخول إلى بوابة Azure.
  2. انتقل إلى Azure Active Directoryتسجيلات التطبيقاتتسجيل جديد.

todo:image_alt_text

  1. أدخل اسمًا لتطبيقك (مثلاً، AsposeEmailGraphApp).
  2. اختر أنواع الحسابات المدعومة:
    • مستأجر واحد (إذا كانت مؤسستك فقط ستستخدمه)
    • متعدد المستأجرين (إذا احتاجت عدة مؤسسات للوصول)
  3. اختياريًا، حدد URI لإعادة التوجيه (مطلوب للمصادقة التفاعلية أو الويب).
  4. انقر تسجيل.

todo:image_alt_text

2. إنشاء سر عميل

  1. بعد التسجيل، انتقل إلى الشهادات والأسرارسر عميل جديد.
  2. أضف وصفًا وفترة انتهاء الصلاحية.

todo:image_alt_text

  1. انسخ قيمة السر المولدة - لن تتمكن من رؤيتها مرة أخرى.

احفظ سر العميل بأمان؛ فهو مطلوب لمصادقة العميل السري.

يجب أن ترى شريط التطبيقات المسجل حديثًا.

todo:image_alt_text

3. تكوين أذونات Microsoft Graph

  1. انتقل إلى أذونات APIإضافة إذنMicrosoft Graph.
  2. اختر نوع الأذونات: مفوضة أو تطبيقية، حسب السيناريو الخاص بك.
  3. أضف الأذونات المطلوبة لعمليات Aspose.Email:
    • Contacts.ReadWrite – لإدارة جهات الاتصال
    • Calendars.ReadWrite – لإدارة التقويمات
    • Mail.ReadWrite – لقراءة وإرسال الرسائل
    • Tasks.ReadWrite – لإدارة المهام
  4. انقر منح موافقة المدير إذا لزم الأمر.

todo:image_alt_text

4. السماح بتدفقات العملاء العامة

حدد ما إذا كان التطبيق عميلًا عامًا. مناسب للتطبيقات التي تستخدم تدفقات منح الرموز ولا تستخدم عنوان URI لإعادة التوجيه.

todo:image_alt_text

5. مصادقة Microsoft Graph

طرق المصادقة المدعومة في Aspose.Email

| موفر الرمز | حالة الاستخدام | | ——————————– | ————————————————————————————— | | AzureConfidentialTokenProvider | عميل سري (معرف العميل + السر) لتطبيقات الخادم | | AzureROPCConfiguration | بيانات اعتماد مالك المورد (اسم المستخدم + كلمة المرور) للسيناريوهات غير التفاعلية | | AzurePublicTokenProvider | عميل عام (تسجيل دخول تفاعلي) | | AzureTokenProviderBase | الفئة الأساسية لتطبيقات المصادقة المخصصة |

المصادقة باستخدام عميل سري

استخدم الـ AzureConfidentialTokenProvider للمصادقة عندما يكون لديك معرف العميل، سر العميل، ومعرف المستأجر:

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.