การใช้ OAuth เพื่อเข้าถึงบริการอีเมล

ได้เพิ่มการสนับสนุน OAuth 2.0 ให้กับ Aspose.Email และสามารถใช้เข้าถึงเซิร์ฟเวอร์ SMTP, POP3, IMAP และ EWS โดยทั่วไป เซิร์ฟเวอร์ทั้งหมดที่สนับสนุนโทเค็น Bearer OAuth 2.0 สามารถใช้กับ Aspose.Email ได้ แต่ไคลเอนต์อีเมลของเราถูกทดสอบกับเซิร์ฟเวอร์อีเมลของ Google และเซิร์ฟเวอร์ Microsoft Office 365 การเข้าถึงเซิร์ฟเวอร์จาก SmtpClient, Pop3Client, ImapClient และ EWSClient ด้วย OAuth สามารถนำไปใช้ได้ 2 วิธี

  1. ให้โทเค็นการเข้าถึงโดยตรงในคอนสตรัคเตอร์ของไคลเอนต์อีเมล ในกรณีนี้ผู้ใช้ต้องเข้าใจว่าช่วงอายุของโทเค็นการเข้าถึงมีจำกัด เมื่อโทเค็นหมดอายุ ไคลเอนต์อีเมลไม่สามารถใช้เข้าถึงเซิร์ฟเวอร์ได้
  2. จัดเตรียมการนำเสนอผู้ให้โทเค็นแบบกำหนดเองโดยอิงจาก ITokenProvider อินเทอร์เฟซเข้าสู่คอนสตรัคเตอร์ของไคลเอนต์อีเมล ในกรณีนี้ไคลเอนต์จะตรวจสอบเวลาหมดอายุของโทเค็นและร้องขอ ITokenProvider สำหรับโทเค็นการเข้าถึงใหม่เมื่อโทเค็นก่อนหน้าหมดอายุ ด้วยวิธีนี้ไคลเอนต์จะรีเฟรชโทเค็นเป็นระยะและอาจทำงานกับเซิร์ฟเวอร์ได้ไม่จำกัดเวลา บริการส่วนใหญ่สนับสนุนวิธีง่ายๆ ในการรีเฟรชโทเค็นการเข้าถึง ตัวอย่างเช่น การใช้รีเฟรชโทเค็นในบริการของ Google หรือกระบวนการตรวจสอบสิทธิ์ ROPC ใน Microsoft identity platform สามารถใช้สำหรับการนำผู้ให้โทเค็นไปใช้ได้

กำหนดค่าบัญชีบนเซิร์ฟเวอร์ที่เหมาะสม

บทความต่อไปนี้ช่วยคุณกำหนดค่าบัญชีเพื่อเข้าถึงบริการอีเมล

เข้าถึงบริการอีเมลด้วยโทเค็นการเข้าถึง

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีเชื่อมต่อบริการอีเมลโดยใช้โทเค็นการเข้าถึง

// Connecting to SMTP server
try (SmtpClient client = new SmtpClient(
        "smtp.gmail.com",
        587,
        "user1@gmail.com",
        "accessToken",
        true,
        SecurityOptions.SSLExplicit)) {

}

// Connecting to IMAP server
try (ImapClient client = new ImapClient(
        "imap.gmail.com",
        993,
        "user1@gmail.com",
        "accessToken",
        true,
        SecurityOptions.SSLImplicit)) {

}

// Connecting to POP3 server
try (Pop3Client client = new Pop3Client(
        "pop.gmail.com",
        995,
        "user1@gmail.com",
        "accessToken",
        true,
        SecurityOptions.Auto)) {

}

เข้าถึงบริการอีเมลด้วยผู้ให้โทเค็น

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีเชื่อมต่อบริการอีเมลด้วยผู้ให้โทเค็น

ITokenProvider tokenProvider = TokenProvider.Google.getInstance(
        "ClientId",
        "ClientSecret",
        "RefreshToken");

// Connecting to SMTP server
try (SmtpClient client = new SmtpClient(
        "smtp.gmail.com",
        587,
        "user1@gmail.com",
        tokenProvider,
        SecurityOptions.SSLExplicit)) {

}

// Connecting to IMAP server
try (ImapClient client = new ImapClient(
        "imap.gmail.com",
        993,
        "user1@gmail.com",
        tokenProvider,
        SecurityOptions.SSLImplicit)) {

}

// Connecting to POP3 server
try (Pop3Client client = new Pop3Client(
        "pop.gmail.com",
        995,
        "user1@gmail.com",
        tokenProvider,
        SecurityOptions.Auto)) {

}

การนำเสนอ Custom ITokenProvider สำหรับ Office 365

คุณสามารถใช้การนำเสนอผู้ให้โทเค็นด้านล่างเพื่อเข้าถึงบริการอีเมลของ Office 365 ได้

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 = geToken();

        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> geToken() {
        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();
    }
}

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีเชื่อมต่อบริการ Office 365 ด้วยผู้ให้โทเค็นแบบกำหนดเอง

ITokenProvider tokenProvider = new AzureROPCTokenProvider(
        "Tenant",
        "ClientId",
        "ClientSecret",
        "EMail",
        "Password",
        scopes);

// Connecting to SMTP server
try (SmtpClient client = new SmtpClient(
        "smtp.office365.com",
        587,
        "Test1@test.onmicrosoft.com",
        tokenProvider,
        SecurityOptions.SSLExplicit)) {

}

// Connecting to IMAP server
try (ImapClient client = new ImapClient(
        "outlook.office365.com",
        993,
        "Test1@test.onmicrosoft.com",
        tokenProvider,
        SecurityOptions.SSLImplicit)) {

}

// Connecting to POP3 server
try (Pop3Client client = new Pop3Client(
        "outlook.office365.com",
        995,
        "Test1@test.onmicrosoft.com",
        tokenProvider,
        SecurityOptions.Auto)) {

}

// Connecting to EWS server
final String mailboxUri = "https://outlook.office365.com/ews/exchange.asmx";
ICredentials credentials = new OAuthNetworkCredential(tokenProvider);
try (IEWSClient ewsClient = EWSClient.getEWSClient(mailboxUri, credentials)) {

}

สิทธิ์ในการเข้าถึง Office 365 ผ่าน IMAP, POP3 หรือ SMTP

เราจำเป็นต้องกำหนดสิทธิ์ API ที่ถูกต้องและมอบความยินยอมของผู้ดูแลระบบเพื่อเข้าถึงบริการอีเมลของ Office 365:

todo:image_alt_text

ในตัวช่วยสร้างสิทธิ์ API / เพิ่มสิทธิ์ ให้เลือก Microsoft Graph แล้วเลือก Delegated permissions เพื่อค้นหาระดับสิทธิ์ต่อไปนี้ที่แสดง:

offline_access
IMAP.AccessAsUser.All
POP.AccessAsUser.All
SMTP.Send

ตัวอย่างผู้ให้โทเค็น:

ITokenProvider tokenProvider = new AzureROPCTokenProvider(OAuth.Tenant, OAuth.ClientId, OAuth.ClientSecret, User.EMail, User.Password,
        new String[] {
                "offline_access",
                "https://outlook.office.com/IMAP.AccessAsUser.All", // IMAP scope
                "https://outlook.office.com/POP.AccessAsUser.All",  // POP3 scope
                "https://outlook.office.com/SMTP.Send"              // SMTP scope
        });