🚨 重要通知:微軟強制 OAuth2,傳統認證已失效!
2023 年 10 月起,Office 365 全面禁用用戶名 + 密碼認證,Java 開發者必須通過OAuth 2.0實現郵件發送。本文針對 CSDN 技術棧,提供從 Azure AD 配置到生產級代碼的全流程方案,附 JDK 8 兼容實現和 Spring Boot 集成示例。
一、Office 365 對接核心流程(圖示)
二、Azure AD 應用注冊(分步教程)
-
創建應用
- 登錄Azure 門戶?→?Azure AD?→?應用注冊?→?新建注冊
- 記錄:
Client ID
、Client Secret
、Tenant ID
(租戶域名或 ID)
-
配置權限
- 在API 權限中添加
Microsoft Graph
的Mail.Send
權限(選擇應用權限) - 點擊授予管理員同意(需管理員賬號操作)
- 在API 權限中添加
-
獲取令牌端點
plaintext
令牌URL:https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token
三、Java 原生 API 實現(JDK 8 兼容)
1. 核心依賴(Maven)
xml
<dependency><groupId>com.sun.mail</groupId><artifactId>mail</artifactId><version>1.6.2</version> <!-- JDK8唯一兼容版本 -->
</dependency>
<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version> <!-- HTTP請求工具 -->
</dependency>
2. 完整代碼示例
java
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;public class Office365MailClient {private static final String TOKEN_URL = "https://login.microsoftonline.com/%s/oauth2/v2.0/token";private static final String SCOPE = "https://outlook.office365.com/.default";private final String tenantId, clientId, clientSecret, senderEmail;private final ConcurrentHashMap<String, String> tokenCache = new ConcurrentHashMap<>();public Office365MailClient(String tenantId, String clientId, String clientSecret, String senderEmail) {this.tenantId = tenantId;this.clientId = clientId;this.clientSecret = clientSecret;this.senderEmail = senderEmail;}public void sendHtmlEmail(String to, String subject, String htmlContent) throws Exception {String accessToken = getAccessToken();Properties props = getSmtpProperties(accessToken);Session session = Session.getInstance(props);MimeMessage msg = createMimeMessage(session, to, subject, htmlContent);try (Transport transport = session.getTransport("smtp")) {transport.connect(); // 自動觸發XOAUTH2認證transport.sendMessage(msg, msg.getAllRecipients());System.out.println("發送成功,響應碼:" + ((SMTPTransport) transport).getLastServerResponse());}}private Properties getSmtpProperties(String accessToken) {Properties props = new Properties();props.put("mail.smtp.host", "smtp.office365.com");props.put("mail.smtp.port", "587");props.put("mail.smtp.auth", "true");props.put("mail.smtp.starttls.enable", "true");props.put("mail.smtp.auth.mechanisms", "XOAUTH2");// 注入OAuth2認證器props.put("mail.smtp.user", senderEmail);props.put("mail.smtp.password", accessToken);return props;}private MimeMessage createMimeMessage(Session session, String to, String subject, String htmlContent) throws MessagingException {MimeMessage msg = new MimeMessage(session);msg.setFrom(new InternetAddress(senderEmail));msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));msg.setSubject(subject, "UTF-8");msg.setContent(htmlContent, "text/html; charset=utf-8");return msg;}// 令牌獲取與緩存(簡化實現)private String getAccessToken() throws Exception {// 實際需實現HTTP請求獲取令牌,參考后文Spring Boot方案return "your_oauth2_token";}
}
四、Spring Boot 集成方案(生產級)
1. 依賴配置
xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId><exclusions><exclusion><groupId>com.sun.mail</groupId><artifactId>javax.mail</artifactId></exclusion></exclusions>
</dependency>
<dependency><groupId>com.sun.mail</groupId><artifactId>javax.mail</artifactId><version>1.6.2</version>
</dependency>
2. 配置文件(application.properties)
properties
spring.mail.host=smtp.office365.com
spring.mail.port=587
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.auth.mechanisms=XOAUTH2office365.tenant-id=your_tenant_id
office365.client-id=your_client_id
office365.client-secret=your_client_secret
office365.sender-email=your_sender@example.com
3. 服務類實現
java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;@Service
public class Office365MailService {private final JavaMailSender mailSender;private final String tenantId, clientId, clientSecret, senderEmail;private final Office365TokenProvider tokenProvider;public Office365MailService(JavaMailSender mailSender,@Value("${office365.tenant-id}") String tenantId,@Value("${office365.client-id}") String clientId,@Value("${office365.client-secret}") String clientSecret,@Value("${office365.sender-email}") String senderEmail) {this.mailSender = mailSender;this.tenantId = tenantId;this.clientId = clientId;this.clientSecret = clientSecret;this.senderEmail = senderEmail;this.tokenProvider = new Office365TokenProvider(tenantId, clientId, clientSecret);}public void sendEmail(String to, String subject, String htmlContent) throws MessagingException {MimeMessage message = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");helper.setFrom(senderEmail);helper.setTo(to);helper.setSubject(subject);helper.setText(htmlContent, true); // 支持HTML// 注入令牌到郵件頭(非必須,Spring自動處理認證)message.setHeader("Authorization", "Bearer " + tokenProvider.getAccessToken());mailSender.send(message);}
}
五、常見錯誤與解決方案
錯誤碼 / 提示 | 原因分析 | 解決方案 |
---|---|---|
535 Authentication failed | 令牌無效或權限不足 | 檢查 Azure AD 應用權限,重新獲取令牌 |
550 5.7.1 SendAs permission | 缺少發送權限 | 通過 PowerShell 分配SendAs 權限 |
421 Service not available | 服務器臨時繁忙 | 添加重試機制,設置指數退避(如 1s→2s→4s) |
ClassNotFoundException: SMTPTransport | JDK 版本不兼容 | 確保使用 JavaMail 1.6.2+,JDK 8 需顯式引入com.sun.mail:mail:1.6.2 |
六、性能優化與安全實踐
-
令牌緩存
使用ConcurrentHashMap
或 Redis 緩存令牌,設置提前 5 分鐘刷新:java
private static final long TOKEN_EXPIRE = 3600; // 有效期1小時 private static final long REFRESH_BUFFER = 300; // 提前5分鐘刷新
-
連接池配置
在 Spring Boot 中配置連接池參數:properties
spring.mail.properties.mail.smtp.connectionpool.size=20 spring.mail.properties.mail.smtp.connectionpool.timeout=5000
-
敏感信息管理
- 禁止硬編碼,使用環境變量或 Spring 配置中心
- 通過
azure-keyvault
組件從 Key Vault 獲取Client Secret
七、權威參考
-
微軟官方文檔
- Office 365 SMTP OAuth2 認證
- Azure AD 應用權限配置
-
JavaMail 最佳實踐
- XOAUTH2 認證示例
- JDK 8 兼容性指南
🌟 總結
本文提供了 Java 對接 Office 365 郵箱的完整解決方案,覆蓋原生 API 和 Spring Boot 集成,特別針對 JDK 8 兼容性和 OAuth 2.0 認證做了深度優化。