我正在使用 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 帐户上进行设置的屏幕截图。
这不是答案!这是另一个问题。
我只是想问一下你解决了吗?我面临同样的问题,几乎没有任何实现示例或文档。您可以尝试使用 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)
谢谢!