Java Oauth2 使用 Office 365 发送电子邮件

问题描述 投票:0回答:1

我正在使用 Java 1.8、Jakarta 电子邮件(版本 2.1.0)连接到 Microsoft 365,并使用 OAuth 通过 SMTP 进行身份验证。客户端需要使用 Oauth 身份验证,而不是基本的 smtp 身份验证。阅读文档 https://learn.microsoft.com/en-us/exchange/client-developer... 以及许多其他资源后,我已将 AD 配置为此权限:

Microsoft Graph    offline_access
Microsoft Graph    User.Read
Microsoft Graph    Mail.Send
Microsoft Graph    openid
Microsoft Graph    IMAP.AccessAsUser.All
Microsoft Graph    SMTP.Send

Office 365 Exchange Online    full_access_as_app
Office 365 Exchange Online    POP.AccessAsApp
Office 365 Exchange Online    Mail.Send
Office 365 Exchange Online    IMAP.AccessAsApp

在 Azure 上使用 PowerShell 激活 SMTP 客户端身份验证 https://learn.microsoft.com/en-us/exchange/clients...

PS C:\Users\dx-2102> Get-TransportConfig | Format-List SmtpClientAuthenticationDisabled   
SmtpClientAuthenticationDisabled : False

发送电子邮件的实现代码(Java):

Properties prop = new Properties();
prop.put("mail.smtp.auth", "true");
prop.put("mail.smtp.starttls.enable", "true");
prop.put("mail.smtp.host", emailSettings.getSmtp().getHostname());
prop.put("mail.smtp.port", emailSettings.getSmtp().getPort());
prop.put("mail.debug", "true");
prop.put("mail.debug.auth", "true");
prop.put("mail.smtp.auth.xoauth2.disable", "false");
prop.put("mail.smtp.auth.mechanisms", "XOAUTH2");
prop.put("mail.transport.protocol", "smtp");
prop.put("mail.smtp.auth.login.disable", "true");
prop.put("mail.smtp.auth.plain.disable", "true");

session = Session.getInstance(prop);
session.setDebug(true);
String accessToken = getOAuth2AccessToken();

transport = session.getTransport("smtp");
transport.connect(emailSettings.getSmtp().getHostname(), emailSettings.getSmtp().getPort(), emailSettings.getSmtp().getUsername(), tokenForSmtp(emailSettings.getSmtp().getUsername(), accessToken));
/* -- */ 
transport.sendMessage(mimeMessage, mimeMessage.getAllRecipients());
transport.close();

从 Azure AD 调用/获取令牌的方法,作为响应,我们得到一个带有过期时间的令牌。

String url = "https://login.microsoftonline.com/" + Tenant_ID + "/oauth2/token";

MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("grant_type", "client_credentials");
map.add("response_type", "code");
map.add("client_id", ClientId);
map.add("client_secret", ClientSecret);
map.add("scope","openid offline_access https%3A%2F%2Foutlook.office365.com%2FSMTP.Send ");

RestTemplate restTemplate = new RestTemplate();
ResponseEntity<AzureResponse> response = restTemplate.postForEntity(url, map, AzureResponse.class);

在 SMTP 发送邮件过程中准备发送令牌的方法

private String tokenForSmtp(String userName, String accessToken) {
    final String ctrlA=Character.toString((char) 1);
    final String coded= "user=" + userName + ctrlA+"auth=Bearer " + accessToken + ctrlA+ctrlA;
    return Base64.getEncoder().encodeToString(coded.getBytes());
    //base64("user=" + userName + "^Aauth=Bearer " + accessToken + "^A^A")
}

发送 SMTP 电子邮件后,我收到错误:

AUTH XOAUTH2 dXNlcj1zb2ZhQHNvbHV0aW9uZmFjdG9yeWFnLm9ub...=
535 5.7.3 Authentication unsuccessful [VI1PR0202CA0024.eurprd02.prod.outlook.com]
Error on sending email: 535 5.7.3 Authentication unsuccessful [VI1PR0202CA0024.eurprd02.prod.outlook.com]

除了 openid、offline_access 和 https://outlook.office.com/SMTP.Send 之外,我是否还需要 Azure AD 中的其他令牌范围?或者我错过了 Azure 配置中的其他内容吗?有人有一个 Java 示例说明如何完成此操作,请随时附上。另外,如果您有一些有关在 Azure 帐户上进行设置的屏幕截图。

java azure oauth-2.0 office365 exchange-server
1个回答
0
投票

这不是答案!这是另一个问题。

我只是想问一下你解决了吗?我面临同样的问题,几乎没有任何实现示例或文档。您可以尝试使用 com.microsoft.aad.msal4j 进行令牌查询:

    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>msal4j</artifactId>
        <version>1.13.10</version>
    </dependency>

对我有用,但仅适用于 .default 或 https://outlook.office365.com/.default 作为范围。

这是我的实现:

    public static void main(String[] args) throws ExecutionException, InterruptedException, MessagingException, MalformedURLException {

    Properties props = new Properties();
    props.put("mail.smtp.port", "587");
    props.put("mail.smtp.host", "smtp.office365.com");
    props.put("mail.imap.ssl.enable", "true");
    props.put("mail.smtp.auth.mechanisms", "XOAUTH2");
    props.put("mail.smtp.starttls.enable", "true");
    props.put("mail.smtp.starttls.required", "true");
    props.put("mail.debug.auth", "true");
    props.put("mail.debug", "true");
    props.put("mail.smtp.auth.xoauth2.disable", "false");
    props.put("mail.transport.protocol", "smtp");
    props.put("mail.smtp.auth.login.disable", "true");
    props.put("mail.smtp.auth.plain.disable", "true");

    Session session = Session.getInstance(props);

    Transport transport = session.getTransport("smtp");

    IAuthenticationResult t = tokenFor("https://outlook.office365.com/.default").get();
    String token = t.accessToken();

    transport.connect("smtp.office365.com", 587, "[email protected]", token);
}

private static CompletableFuture<IAuthenticationResult> tokenFor(String... scopes) throws MalformedURLException {

    String tenantId = "...";
    String clientId = "...";
    String url = "https://login.microsoftonline.com/%s/oauth2/v2.0/token";

    String authority = String.format(url, tenantId);

    String clientSecret = "...";

    IClientCredential secret = ClientCredentialFactory.createFromSecret(clientSecret);

    ConfidentialClientApplication.Builder app = ConfidentialClientApplication.builder(clientId, secret)
            .authority(authority);

    ClientCredentialParameters.ClientCredentialParametersBuilder clientCredentialParam =
            ClientCredentialParameters.builder
                    (new HashSet<>(Arrays.asList(scopes)));

    return app.build().acquireToken(clientCredentialParam.build());
}

有人可以试试这个吗?

我收到以下错误:

5.7.3 Authentication unsuccessful [........PROD.OUTLOOK.COM 2023-10-07T15:04:28.150Z ....]
Exception in thread "main" javax.mail.AuthenticationFailedException: 535 5.7.3 Authentication unsuccessful [........PROD.OUTLOOK.COM 2023-10-07T15:04:28.150Z ...]

at com.sun.mail.smtp.SMTPTransport$Authenticator.authenticate(SMTPTransport.java:947)
at com.sun.mail.smtp.SMTPTransport.authenticate(SMTPTransport.java:858)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:762)
at javax.mail.Service.connect(Service.java:366)

谢谢!

© www.soinside.com 2019 - 2024. All rights reserved.