Cài đặt Azure AD và Xác thực Microsoft Graph

Aspose.Email for Java cung cấp tích hợp đầy đủ với Microsoft Graph, cho phép các nhà phát triển quản lý tin nhắn, danh bạ, lịch và nhiệm vụ từ tài khoản Microsoft 365. Hướng dẫn này sẽ chỉ cho bạn cách tạo một ứng dụng Azure AD và cấu hình xác thực để bắt đầu lập trình với Aspose.Email GraphClient.

Trước khi sử dụng các API Microsoft Graph với Aspose.Email, bạn cần đăng ký một ứng dụng trong Azure Active Directory (Azure AD) và cấu hình xác thực. Trang này bao gồm:

  • Tạo một ứng dụng Azure AD (dự án).

  • Gán các quyền Microsoft Graph cần thiết.

  • Tạo thông tin xác thực (client ID, client secret, tenant ID).

  • Xác thực trong Java bằng các nhà cung cấp token Aspose.Email.

Khi hoàn thành, bạn sẽ sẵn sàng tương tác với Microsoft Graph từ ứng dụng Java của mình.

1. Tạo một Ứng dụng Azure AD

Thực hiện các bước sau để đăng ký ứng dụng của bạn trong Azure portal:

  1. Đăng nhập vào Cổng Azure.
  2. Đi tới Azure Active DirectoryApp RegistrationsNew Registration.

todo:image_alt_text

  1. Nhập Tên cho ứng dụng của bạn (ví dụ: AsposeEmailGraphApp).
  2. Chọn các loại tài khoản được hỗ trợ:
    • Thuê đơn (nếu chỉ tổ chức của bạn sẽ sử dụng)
    • Đa thuê (nếu nhiều tổ chức cần truy cập)
  3. Tùy chọn, thiết lập Redirect URI (cần cho xác thực tương tác hoặc web).
  4. Nhấn Register.

todo:image_alt_text

2. Tạo Client Secret

  1. Sau khi đăng ký, vào Certificates & SecretsNew Client Secret.
  2. Thêm mô tả và thời gian hết hạn.

todo:image_alt_text

  1. Sao chép giá trị secret đã tạo - bạn sẽ không thể xem lại.

Giữ client secret an toàn; nó cần thiết cho xác thực client bí mật.

Bạn sẽ thấy blade các ứng dụng vừa đăng ký.

todo:image_alt_text

3. Cấu hình Quyền Microsoft Graph

  1. Đi tới API PermissionsAdd a PermissionMicrosoft Graph.
  2. Chọn loại quyền: Delegated hoặc Application, tùy thuộc vào kịch bản của bạn.
  3. Thêm các quyền cần thiết cho các hoạt động của Aspose.Email:
    • Contacts.ReadWrite – để quản lý danh bạ
    • Calendars.ReadWrite – để quản lý lịch
    • Mail.ReadWrite – để đọc và gửi tin nhắn
    • Tasks.ReadWrite – để quản lý tác vụ
  4. Nhấn Grant Admin Consent nếu cần.

todo:image_alt_text

4. Cho phép luồng client công cộng

Chỉ định liệu ứng dụng là client công cộng hay không. Thích hợp cho các ứng dụng sử dụng luồng cấp token mà không sử dụng redirect URI.

todo:image_alt_text

5. Xác thực Microsoft Graph

Các phương thức xác thực được hỗ trợ trong Aspose.Email

| Nhà cung cấp Token | Trường hợp sử dụng | | ——————————– | ————————————————————————————— | | AzureConfidentialTokenProvider | Client bí mật (client ID + secret) cho các ứng dụng phía máy chủ | | AzureROPCConfiguration | Resource Owner Password Credentials (tên người dùng + mật khẩu) cho các kịch bản không tương tác | | AzurePublicTokenProvider | Client công cộng (đăng nhập tương tác) | | AzureTokenProviderBase | Lớp cơ sở cho các triển khai xác thực tùy chỉnh |

Xác thực bằng Client Bí mật

Sử dụng AzureConfidentialTokenProvider để xác thực khi bạn có client ID, client secret và 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");

Điều này thiết lập một IGraphClient đã được xác thực đầy đủ, sẵn sàng tương tác với Microsoft Graph.

Xác thực bằng ROPC (Tên người dùng & Mật khẩu)

Đối với các trường hợp có tên người dùng và mật khẩu, sử dụng 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");

Nhà cung cấp Token Tùy chỉnh cho Microsoft Graph

Aspose.Email for Java tích hợp với Microsoft Graph thông qua IGraphClient giao diện. Để xác thực yêu cầu, một triển khai của ITokenProvider được yêu cầu. Trong khi hầu hết các nhà phát triển sẽ sử dụng các nhà cung cấp xác thực tích hợp, vẫn có những trường hợp bạn muốn tự tạo nhà cung cấp của mình, ví dụ khi làm việc với luồng Resource Owner Password Credentials (ROPC).

1. Triển khai ITokenProvider bằng AzureROPCTokenProvider

Lớp này cung cấp một triển khai của ITokenProvider sử dụng luồng Azure Resource Owner Password Credentials (ROPC). Ví dụ dưới đây chỉ mang tính minh họa. Trong môi trường sản xuất, chúng tôi đề nghị sử dụng các luồng an toàn hơn như client credentials hoặc authorization code với 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. Tạo Đối tượng ITokenProvider

Cái IGraphClient giao diện chịu trách nhiệm xây dựng các yêu cầu, gửi chúng tới Microsoft Graph và xử lý các phản hồi. Để tạo một thể hiện của IGraphClient, bạn phải cung cấp một triển khai của ITokenProvider. Nhà cung cấp token xác thực các yêu cầu bằng cách cung cấp một token truy cập OAuth hợp lệ.

Đoạn mã mẫu dưới đây minh họa cách tạo một triển khai nội tuyến cơ bản của ITokenProvider giao diện, cần thiết để xác thực các yêu cầu 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. Sử dụng Nhà cung cấp Token Tùy chỉnh

Khi ITokenProvider đã được thiết lập, bạn có thể tạo một GraphClient đối tượng. Khách hàng này sẽ sử dụng token provider được cung cấp để xác thực khi gọi 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());
}

Sau khi khách hàng được tạo và xác thực, bạn có thể bắt đầu gửi yêu cầu tới dịch vụ Microsoft Graph.