การตั้งค่า 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 ที่จำเป็น.

  • สร้างข้อมูลรับรอง (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. เลือกประเภทบัญชีที่รองรับ:
    • Single tenant (หากมีเพียงองค์กรของคุณเท่านั้นที่จะใช้)
    • Multitenant (หากหลายองค์กรต้องการเข้าถึง)
  3. หากต้องการ สามารถตั้งค่า Redirect URI (จำเป็นสำหรับการรับรองแบบโต้ตอบหรือเว็บ)
  4. คลิก Register.

todo:image_alt_text

2. สร้าง Client Secret

  1. หลังจากลงทะเบียน ไปที่ Certificates & SecretsNew Client Secret.
  2. เพิ่มคำอธิบายและระยะเวลาหมดอายุ.

todo:image_alt_text

  1. คัดลอกค่าความลับที่สร้างขึ้น - คุณจะไม่เห็นอีกครั้ง

เก็บ client secret ไว้อย่างปลอดภัย; จำเป็นสำหรับการรับรองของ confidential client.

คุณควรเห็นแถบแอปที่ลงทะเบียนใหม่

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 | Confidential client (client ID + secret) สำหรับแอปด้านเซิร์ฟเวอร์ | | AzureROPCConfiguration | Credential ของเจ้าของทรัพยากร (ชื่อผู้ใช้ + รหัสผ่าน) สำหรับสถานการณ์ที่ไม่โต้ตอบ | | AzurePublicTokenProvider | ลูกค้าแบบสาธารณะ (เข้าสู่ระบบแบบโต้ตอบ) | | AzureTokenProviderBase | คลาสฐานสำหรับการทำงานการรับรองแบบกำหนดเอง |

รับรองด้วย Confidential Client

ใช้ 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) ตัวอย่างต่อไปนี้ใช้เพื่อการสาธิตเท่านั้น ในการใช้งานจริง เราขอแนะนำให้ใช้การไหลที่ปลอดภัยกว่าเช่น client credentials หรือ authorization code กับ 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 ได้