Câu chuyện là tự nhiên, không hiểu vì một vấn đề gì đó mà Microsoft chặn không cho máy chủ của mình gửi mail thông qua SMTP nữa. Kết quả khi gửi mail bằng java phải đợi một lúc lâu mới có phản hồi:
com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.office365.com, 587; timeout -1

sử dụng lệnh: telnet smtp.office365.com thì kết quả chỉ nhận được timeout.

Vậy nên mình đã nghiên cứu một phương pháp mới sử dụng Microsoft Graph API và chia sẻ cho mọi người. Nó sẽ bao gồm các bước:

  1. Đăng ký tài khoản azure.
  2. Tạo ứng dụng.
  3. Cấp quyền gửi mail.
  4. Mã nguồn gọi API.

Đăng ký tài khoản auzre

Bạn sẽ cần thực hiện các bước sau:

1. Hãy đảm bảo bạn đã có tài khoản office 365 doanh nghiệp. Bạn có thể đăng ký gói cơ bản.

Screenshot 2025-05-29 at 10.22.42.png

2. Tiếp theo bạn có thể truy cập https://portal.azure.com và tạo tài khoản, sau khi tạo tài khoản xong bạn có thể thấy giao diện:

Screenshot 2025-05-29 at 10.25.28.png

Hãy dành sự chú ý đến ô tìm kiếm.

Tạo ứng dụng

3. Tiếp theo bạn hãy vào Azure Active Directory > App registrations bằng cách nhập vào ô tìm kiếm App registrations và chọn kết quả đầu tiên mà bạn tìm được:

Screenshot 2025-05-29 at 10.30.21.png

Kết quả bạn nhận được một giao diện mới có nút New registration:

Screenshot 2025-05-29 at 10.33.23.png

4. Tiếp theo bạn hãy đăng ký một ứng dụng bằng cách nhấn vào nút New registrationmột giao diện sẽ hiện ra và bạn có thể điền các thông tin cần thiết, ví dụ:

Screenshot 2025-05-29 at 10.37.06.png

Sau đó nhấn nút Register bạn sẽ nhận được một giao diện kết quả với nút Add a certificate or secret như sau:

Screenshot 2025-05-29 at 10.40.41.png

5. Tiếp theo hãy nhấn vào nút Add a certificate or secret để mở giao diện tạo khoá bí mật.

Screenshot 2025-05-29 at 10.44.13.png

6. Tiếp theo bạn hãy nhấn vào nút New client secret để điền thông tin.

Screenshot 2025-05-29 at 11.10.56.png

Sau đó nhấn nút Add để tạo khoá bí mật.

Bạn hãy sao chép khoá bí mật trong cột value và lưu vào đâu đó vì khoá bí mật này chỉ xuất hiện duy nhất 1 lần:

Screenshot 2025-05-29 at 11.12.26.png

Sau đó bạn hãy quay trở lại ứng dụng bằng cách nhấn vào menu item Overview:

Screenshot 2025-05-29 at 11.16.52.png

Tổng kết lại sau bước tạo ứng dụng bạn đã có:

  1. Mã ứng dụng (TENANT_ID): Là giá trị của trường Directory (tenant) ID được lấy ở màn hình chi tiết ứng dụng.
  2. Mã khách (CLIENT_ID): Là giá trị của trường Application (client) ID được lấy ở màn hình chi tiết ứng dụng.
  3. Khoá bí mật (CLIENT_SECRET): Là khoá chỉ xuất hiện 1 lần mà bạn đã lưu.

3. Cấp quyền gửi mail.

Bây giờ bạn hãy quay lại office 365 admin center, ở menu bạn hãy nhấn vào nút Hiển thị tất cả:

Screenshot 2025-05-29 at 11.24.24.png

Bạn sẽ thấy có thêm các menu mới hiện ra, trong đó có menu Exchange:

Screenshot 2025-05-29 at 11.26.15.png

Bạn hãy click vào menu này, giao diện danh sách các tài khoản sẽ được hiện ra. Bạn hãy chọn một tài khoản mà bạn muốn dùng để gửi email để cấu hình.

Khi giao diện cấu hình hiển thị bạn hãy chọn vào mục Thư để thấy giao diện cấu hình thư như sau:

Screenshot 2025-05-29 at 11.31.50.png

Tiếp theo bạn hãy nhấn vào nút Quản lý ứng dụng email. Bạn có thể chọn toàn bộ các ứng dụng:

Screenshot 2025-05-29 at 11.32.09.png

4. Mã nguồn gọi API.

Sau khi đã cấu hình xong mọi thứ, giờ là lúc bạn có thể sử dụng thư viện ezyhttp-client để gọi API gửi mail với mã nguồn như sau:

import com.tvd12.ezyfox.util.EzyMapBuilder;
import com.tvd12.ezyhttp.client.HttpClient;
import com.tvd12.ezyhttp.client.request.PostRequest;
import com.tvd12.ezyhttp.client.request.RequestEntity;
import com.tvd12.ezyhttp.core.response.ResponseEntity;

import java.util.Map;

public class GraphEmailSender {

    private static final String TENANT_ID = "Thay bằng directory id của bạn";
    private static final String CLIENT_ID = "Thay bằng application id của bạn";
    private static final String CLIENT_SECRET = "Thay bằng khoá bí mật của bạn";
    private static final String SENDER_EMAIL = "Thay bằng email mà bạn đã cấu hình";
    private static final String RECIPIENT_EMAIL = "Thay bằng email của người nhận";

    public static void main(String[] args) {
        try {
            String accessToken = getAccessToken();
            sendEmail(accessToken);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String getAccessToken() throws Exception {
        Map<String, Object> form = EzyMapBuilder
            .mapBuilder()
            .put("client_id", CLIENT_ID)
            .put("scope", "https://graph.microsoft.com/.default")
            .put("client_secret", CLIENT_SECRET)
            .put("grant_type", "client_credentials")
            .toMap();
        RequestEntity entity = RequestEntity.builder()
            .contentType("application/x-www-form-urlencoded")
            .body(form)
            .build();
        String url = "https://login.microsoftonline.com/" +
            TENANT_ID +
            "/oauth2/v2.0/token";
        PostRequest request = new PostRequest()
            .setURL(url)
            .setEntity(entity);

        Map<String, Object> response = HttpClient.builder()
            .build()
            .call(request);
        return (String) response.get("access_token");
    }

    private static void sendEmail(String accessToken) throws Exception {
        Map<String, Object> emailBody = EzyMapBuilder
            .mapBuilder()
            .put("message", EzyMapBuilder
                .mapBuilder()
                .put("subject", "Test email from Microsoft Graph API (EzyHttp)")
                .put("body", EzyMapBuilder
                    .mapBuilder()
                    .put("contentType", "Text")
                    .put("content", "Hello, this is a test email sent via Microsoft Graph API using EzyHttp.")
                    .toMap())
                .put("toRecipients", new Object[] {
                    EzyMapBuilder
                        .mapBuilder()
                        .put("emailAddress", EzyMapBuilder
                            .mapBuilder()
                            .put("address", RECIPIENT_EMAIL)
                            .build())
                        .toMap()
                })
                .toMap())
            .toMap();
        RequestEntity entity = RequestEntity.builder()
            .header("Authorization", "Bearer " + accessToken)
            .header("Content-Type", "application/json")
            .body(emailBody)
            .build();
        String url = "https://graph.microsoft.com/v1.0/users/" +
            SENDER_EMAIL +
            "/sendMail";
        PostRequest request = new PostRequest()
            .setEntity(entity)
            .setURL(url);
        ResponseEntity response = HttpClient.builder().build().request(request);
        System.out.println("Status: " + response.getStatus());
        System.out.println("Response: " + response.getBody());
    }
}

Khi bạn nhận được Status: 202 nghĩa là thành công.

Tổng kết

Vì nhiều lý do mà Microsoft hạn chế việc gửi email thông qua smtp, vậy nên phương án sử dụng Microsoft Graph API là một giải pháp thay thế. Tuy nhiên các bước cấu hình tương đối phức tạp, nằm rải rác ở nhiều giao diện khác nhau, khi tích hợp thì có nhiều lỗi khó hiểu. Hy vọng bài viết này sẽ giúp công việc tích hợp của bạn sẽ trở nên đơn giản hơn.