一、JavaMail與SMTP
通常我們使用JavaMail,都是通過SMTP服務器來發送郵件,比如說我有一個domain1.com的郵件賬號,想給domain2.com的郵箱發送郵件。示意圖如下:
圖一:
用戶A首先將需要發送的郵件通過SMTP協議發送給他所在的郵件服務器domain1,然後domain1判斷收件人所在的域為domain2,於是domain1通過SMTP協議再重新將郵件發送到domain2。最後用戶B連接到自己的郵件服務器domain2,接收郵件。通常domain1會將郵件緩存,以便出錯時重新發送,如果重試幾次後還是發送失敗,可能會給用戶A發送一封郵件以告知郵件發送失敗。如果用戶A是一個應用的話,很難知道郵件發送成功與否。
另外一種做法是,用戶(或者我們自己的應用)直接連接對方的SMTP服務器來發送郵件,其實就是連接到對方的25端口,然後按順序發送一些SMTP的命令。詳細的SMTP信息,大家可以查閱相應資料。JavaMail對這些底層的數據傳輸做了很好的包裝。結構示意圖如下:
圖二:
只要用戶A發送的數據格式和前面domain1發送的相同,對於domain2來說,沒什麼區別。而且在這種方式下,如果郵件發送失敗,比如收件人地址不存在,用戶A會馬上得到郵件發送失敗信息。所以在實際的應用中,這種方式比較常見。但是這種方式需要知道對方的SMTP服務器的地址,在Windows下,可以在命令行執行nslookup,輸入 set type=mx,然後輸入需要查找的域名,比如hotmail.com,就可以查找到它的SMTP服務器地址。同樣在Java中,可以通過JNDI或者開源的dnsJava查找收件人所在域的SMTP服務器地址。這裡給出一個簡單的例子。
public class JavaMailSendingHandler implements ILocalEmailSendingHandler {
public void send(String to, String from, String subject, String content, String contentType) throws Exception {
Properties props = new PropertIEs();
props.put("mail.smtp.localhost", getHost(from));
String server = getSMTPServerByJNDI(getHost(to));
props.setProperty("mail.smtp.host", server);
Session session = Session.getInstance(props, null);
MimeMessage message = new MimeMessage(session);
message.setContent(content, contentType);
message.setSender(new InternetAddress(from));
message.setFrom(new InternetAddress(from));
message.setRecipient(RecipIEntType.TO, new InternetAddress(to));
message.setSubject(subject);
message.setHeader("Content-Type", contentType);
message.setHeader("Content-Transfer-Encoding", "7bit");
SimpleDateFormat format = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
message.setHeader("Date", format.format(new Date()));
Transport.send(message, new Address[] { new InternetAddress(to) });
}
private String getHost(String address) {
return address.substringaddress.indexOf('@') + 1);
}
//通過JNDI 查找給定域的郵件服務器。
private String getSMTPServerByJNDI(String host) throws Exception {
Properties jndiEnvironmentProperties = new PropertIEs();
jndiEnvironmentPropertIEs.put("Java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
DirContext initialDirContext = new InitialDirContext(jndiEnvironmentPropertIEs);
Attributes attributes = initialDirContext.getAttributes(host, new String[] {"MX"});
Attribute attribute = attributes.get("MX");
String[] servers = new String[attribute.size()];
for (int i = 0; i < attribute.size(); i++) {
servers[i] = attribute.get(i).toString();
}
String server = servers[0];
server = server.substring(server.indexOf(" ") + 1, servers[0].length() - 1);
return server;
}
}
二、反垃圾郵件機制
最簡單的反垃圾郵件機制就是檢查郵件的每個頭信息是否短缺,格式是否正確。另外比較復雜的一種是SPF(Sender Policy Framework),它主要用來防止偽造的郵件地址。還是采用前面的例子說明,在用戶直接給domain2發送郵件時,需要告示郵件發送者地址,這樣 domain2會根據郵件發送者的地址所在的域名,查找到該域的有效的郵件服務器地址。如果用戶所在的計算機不再查找的結果之中,那就說明,發件人地址是偽造的,從而斷定是垃圾郵件。
為了能夠通過收件人郵件服務器的SPF檢查,需要向所在域的域控制器添加相應的SPF信息。如下圖所示。
圖三
在Windows服務器下,可以通過添加TXT記錄來完成。這裡就不再詳細說明。