JSP 发送邮件
在 JSP 里用 JavaMail 发信、HTML 邮件与附件、常见失败原因。
依赖
<dependency>
<groupId>jakarta.mail</groupId>
<artifactId>jakarta.mail-api</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.eclipse.angus</groupId>
<artifactId>angus-mail</artifactId>
<version>2.0.3</version>
<scope>runtime</scope>
</dependency>jakarta.mail-api is interfaces only; a runtime implementation (Angus Mail or the GlassFish one) is required. With the API alone you get Provider not found at runtime.
发一封纯文本邮件
<%@ page contentType="text/html;charset=UTF-8"
import="jakarta.mail.*, jakarta.mail.internet.*, java.util.*" %>
<%
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.example.com");
props.put("mail.smtp.port", "587");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
final String user = System.getenv("MAIL_USER");
final String pass = System.getenv("MAIL_PASS");
Session mail = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, pass);
}
});
Message msg = new MimeMessage(mail);
msg.setFrom(new InternetAddress(user));
msg.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(request.getParameter("to")));
msg.setSubject("注册确认", "UTF-8");
msg.setText("请点击链接完成注册。", "UTF-8");
Transport.send(msg);
%>
<p>邮件已发送</p>HTML 邮件与附件
MimeMessage msg = new MimeMessage(mail);
msg.setSubject("月度报告", "UTF-8");
Multipart mixed = new MimeMultipart("mixed");
MimeBodyPart text = new MimeBodyPart();
text.setText("您的客户端不支持 HTML。", "UTF-8");
MimeBodyPart html = new MimeBodyPart();
html.setContent("<h1>月度报告</h1><p>详见附件。</p>",
"text/html;charset=UTF-8");
MimeMultipart alt = new MimeMultipart("alternative");
alt.addBodyPart(text);
alt.addBodyPart(html);
MimeBodyPart body = new MimeBodyPart();
body.setContent(alt);
mixed.addBodyPart(body);
MimeBodyPart file = new MimeBodyPart();
file.attachFile("/data/report.pdf");
file.setFileName(MimeUtility.encodeText("月度报告.pdf", "UTF-8", "B"));
mixed.addBodyPart(file);
msg.setContent(mixed);
Transport.send(msg);alternative means the same content in several formats for the client to choose; mixed means parallel parts such as attachments. Reversing them shows two bodies plus an unreadable attachment name.
常见失败原因
| Symptom | Cause |
|---|---|
| 535 auth failed | 用了登录密码而非授权码 |
| Connection timed out | 25 端口被云厂商封禁 |
| 550 relay denied | 发件人与认证账号域名不一致 |
| Subject garbled | 未指定 UTF-8 |
| Goes to spam | SPF / DKIM / DMARC 未配 |
账号密码放哪
Never hardcode credentials in a JSP — the source ships inside the war, can be decompiled, and may be committed to Git. Use environment variables, context-param or a JNDI resource supplied by the deployment.
依赖
<dependency>
<groupId>jakarta.mail</groupId>
<artifactId>jakarta.mail-api</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.eclipse.angus</groupId>
<artifactId>angus-mail</artifactId>
<version>2.0.3</version>
<scope>runtime</scope>
</dependency>jakarta.mail-api 只有接口,运行时要一个实现(Angus Mail 或 GlassFish 的实现)。只加 API 依赖,运行时会报 Provider not found。
发一封纯文本邮件
<%@ page contentType="text/html;charset=UTF-8"
import="jakarta.mail.*, jakarta.mail.internet.*, java.util.*" %>
<%
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.example.com");
props.put("mail.smtp.port", "587");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
final String user = System.getenv("MAIL_USER");
final String pass = System.getenv("MAIL_PASS");
Session mail = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(user, pass);
}
});
Message msg = new MimeMessage(mail);
msg.setFrom(new InternetAddress(user));
msg.setRecipients(Message.RecipientType.TO,
InternetAddress.parse(request.getParameter("to")));
msg.setSubject("注册确认", "UTF-8");
msg.setText("请点击链接完成注册。", "UTF-8");
Transport.send(msg);
%>
<p>邮件已发送</p>HTML 邮件与附件
MimeMessage msg = new MimeMessage(mail);
msg.setSubject("月度报告", "UTF-8");
Multipart mixed = new MimeMultipart("mixed");
MimeBodyPart text = new MimeBodyPart();
text.setText("您的客户端不支持 HTML。", "UTF-8");
MimeBodyPart html = new MimeBodyPart();
html.setContent("<h1>月度报告</h1><p>详见附件。</p>",
"text/html;charset=UTF-8");
MimeMultipart alt = new MimeMultipart("alternative");
alt.addBodyPart(text);
alt.addBodyPart(html);
MimeBodyPart body = new MimeBodyPart();
body.setContent(alt);
mixed.addBodyPart(body);
MimeBodyPart file = new MimeBodyPart();
file.attachFile("/data/report.pdf");
file.setFileName(MimeUtility.encodeText("月度报告.pdf", "UTF-8", "B"));
mixed.addBodyPart(file);
msg.setContent(mixed);
Transport.send(msg);alternative 表示同一内容的不同格式,客户端自己挑;mixed 表示并列的多个部分,用于附件。嵌套顺序反了,邮件会显示成两段正文加一个看不懂的附件名。
常见失败原因
| 现象 | 原因 |
|---|---|
| 认证失败 | 用了登录密码而非授权码 |
| 连接超时 | 25 端口被云厂商封禁 |
| 拒绝转发 | 发件人与认证账号域名不一致 |
| 主题乱码 | 未指定 UTF-8 |
| 进垃圾箱 | SPF / DKIM / DMARC 未配 |
账号密码放哪
绝对不要写死在 JSP 里——JSP 源码可能被打包进 war、被反编译、被提交到 Git。用环境变量、context-param 或 JNDI 资源,让部署环境提供。