介紹
其實郵件推送就比較簡單了,其實可以使用自己的郵件賬號在代碼中使用SMTP
、POP3
或者IMAP
登陸,其實本來也是打算這麼幹的,因爲很多郵件推送服務也是要收費的,但是呢~~阿里雲(又是阿里雲)有個活動
放這個圖着實有點打廣告的嫌疑,但是站在個人角度確實挺優惠
因爲使用大企業已經成熟的郵件推送服務,穩定性肯定不用擔心,而且聽說遇到問題還可以諮詢技術顧問,今天我就收到了阿里雲客服的電話問使用情況並告知有技術問題可以點擊右上角諮詢,服務還是蠻棒的!
這個活動每天可以免費發200
封郵件,參考套餐包的價格是50000
封81
元,哪有用戶一天到晚註冊,一天到晚忘記密碼,而且要是真的有這麼多用戶再考慮付費或者自己實現郵件推送功能吧(哪有穩定什麼的區別,主要是怕麻煩,能用就行…
添加域名
要注意的是,用來發送郵件推送的郵箱域名必須是自己擁有的或者能控制DNS
解析的
試想如果一個用戶收到[email protected]發來的驗證碼,那該有多害怕啊…
國內也有很多域名提供商,比如騰訊雲(新網的代理),阿里雲(旗下品牌萬網),但是要備案
推薦使用https://www.namesilo.com進行域名購買,原因是
- 國外的域名提供商不用備案!
- 價格最便宜,同樣域名比國內域名商都要便宜!(
所以網站UI比較差,這不關鍵) - 支持支付寶
這裏得聲明下,備案的政策還是好的,可以防止一些非法網站的存在,只是我們這種超級小型網站就沒有必要把時間花在這個上面,之前我在騰訊雲也備案過幾個域名,大概週期20多天吧,還要身份認證、幕布拍照什麼的,當然如果運營的好的比較穩定的網站,背個案還是很關鍵的,比如我的博客網站,嘿嘿嘿
註冊域名過程就不說了,註冊完成之後如果覺得DNS解析比較慢或者不穩定或者網站UI看不下去可以 把DNS
解析到CloudFlare
添加下面兩行記錄解析到CloudFlare
,以後就去CloudFlare
管理DNS
記錄了,一時就是把namesilo
當賣域名的,後來除了續費就不用進這個網站了
fred.ns.cloudflare.com
robin.ns.cloudflare.com
當然也可以不設置DNS解析到其它站點,
namesilo
就可以,但是生效比較慢,操作方法爲點擊下圖中的DNS Records
現在來修改DNS
到阿里雲
在郵件推送控制檯->發件域名->新建域名->輸入域名
我這裏使用的是service.域名
它提醒不要用企業郵箱的域名,可能會導致企業郵箱收件異常
然後點擊配置,它會提示你需要添加哪些DNS
記錄,必要的這三條就好了,第四條跟蹤郵件需要備案
如果不會配置DNS
,可以點擊它的示例
我在CloudFlare
的配置如下
這是第四條記錄,非必要,有黃色雲朵表示開啓CDN
,因爲在海外其實會比較慢,推薦關閉
等一會兒,手動點擊發信域名的驗證,如果狀態變爲
可使用-未備案 或 可使用-未驗證CName 或 驗證通過表示可以發送郵件了
添加發件地址
回到郵件推送控制檯->發信地址->新建發信地址
選擇發信域名,賬號,發信類型
回信地址可以不寫
官方說
“觸發類郵件指註冊激活、密碼找回等;批量郵件指營銷推廣、訂閱期刊等。不同類型郵件的發送限制不同,請根據郵件類型選擇。”
其實在發送的時候都是一樣的,我還以爲觸發郵件可以自定義觸發的條件呢,找了半天也沒找到。。。。。。
如果郵件的狀態變爲正常後,表示可以發件了
添加訪問密鑰
使用代碼調用API時,要通過accessKeyId
和secret
驗證身份
點擊右上角個人頭像,選擇訪問控制
爲什麼是點訪問控制而不是
accesskeys
呢?
- 因爲
accesskeys
裏面也會建議你使用子用戶進行訪問。那爲什麼要子用戶進行訪問呢?
- 比如在阿里雲開通了很多付費服務都需要
accesskey
驗證,如果使用主賬戶的accesskey
不小心泄露了,那麼所有的付費服務都被泄露了,可能帶來很大的財產損失和信息丟失- 如果使用子用戶就可以動態控制每個用戶的權限,比如a用戶只有發短息的權限,再不濟泄露了也不會影響其他的服務,將損失降爲最小
選擇用戶->新建用戶,可以看到我已經建立了兩個用戶分別用於短信和郵件服務
寫好登錄名稱,顯示名稱,勾選編程訪問就好了,如果是控制檯登錄的話其實就相當於一個阿里雲賬戶可以在網站上登錄,這個暫時不需要
下圖中的AccessKeyID
和AccessKeySceret
就是編程需要的參數了
不要試圖使用我的這個
AccessKey
幹壞事哦,因爲我賬戶裏沒錢它已經被刪掉啦
需要注意的是賬號一旦創建完成,就再也看不到它們了,所以要提前點擊複製或者下載CSV文件進行備份
然後再點擊添加權限
對於郵件推送
輸入mail
進行搜索,選擇AliyunDirectMailAccess
,點擊確定
使用代碼調用郵件服務
有兩種調用郵件推送的方式
一種是使用阿里雲給的API,只需要提供必要的參數;
另一種是使用SMTP
就相當於阿里雲提供了一個發件服務器,然後你使用代碼登錄進去發郵件,我這裏選擇第一種
添加依賴
以JAVA
的maven
爲例
先要添加一個maven
倉庫
<repositories>
<repository>
<id>sonatype-nexus-staging</id>
<name>Sonatype Nexus Staging</name>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
然後需要添加兩個依賴
如果短信推送服務已經添加了一個,這裏就只需要添加一個
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dm</artifactId>
<version>3.1.0</version>
</dependency>
完整代碼
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dm.model.v20151123.SingleSendMailRequest;
import com.aliyuncs.dm.model.v20151123.SingleSendMailResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
//import com.aliyuncs.http.MethodType;
public void sample() {
// 如果是除杭州region外的其它region(如新加坡、澳洲Region),需要將下面的"cn-hangzhou"替換爲"ap-southeast-1"、或"ap-southeast-2"。
//下面填寫密鑰
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<your accessKey>", "<your accessSecret>");
IAcsClient client = new DefaultAcsClient(profile);
SingleSendMailRequest request = new SingleSendMailRequest();
//使用https加密連接
request.setProtocol(com.aliyuncs.http.ProtocolType.HTTPS);
try {
//request.setVersion("2017-06-22");// 如果是除杭州region外的其它region(如新加坡region),必須指定爲2017-06-22
request.setAccountName("控制檯創建的發信地址");
request.setFromAlias("發信人暱稱");
request.setAddressType(1);
//可以不需要
//request.setTagName("控制檯創建的標籤");
//是否需要回信功能
//request.setReplyToAddress(true);
request.setToAddress("目標地址");
//可以給多個收件人發送郵件,收件人之間用逗號分開,批量發信建議使用BatchSendMailRequest方式
//request.setToAddress("郵箱1,郵箱2");
request.setSubject("郵件主題");
request.setHtmlBody("郵件正文");
SingleSendMailResponse httpResponse = client.getAcsResponse(request);
} catch (ServerException e) {
e.printStackTrace();
}
catch (ClientException e) {
e.printStackTrace();
}
}
不要進入垃圾箱
可以看到,郵件推送服務沒有使用模板,所有內容都是在setHtmlBody
中傳遞,我測試了一下,收到的郵件成功地進入了垃圾箱
郵件爲什麼會進入垃圾箱呢?
知乎有很專業的回答https://www.zhihu.com/question/19574247
對於我們這種情況,最大的原因是
- 郵件內容格式不友好,郵件服務器不喜歡沒有格式的郵件,最好是
html
格式 - 郵件沒有退訂按鈕(這個也不算主要原因,第一點改完基本就可以了)
那麼怎麼發送html
郵件呢,在 request.setHtmlBody("郵件正文");
小小的括號裏寫上一個html
文件未免太不友好,這就需要我們手動創建郵件模板了
使用html模板發送郵件
在resources/static
裏寫上兩個html
文件作爲郵件的模板,一個用於用戶註冊,一個用於用戶找回密碼
要注意不要寫死內容,要留一些變量
那麼怎麼使用它呢?
新建兩個靜態常量數組,用來保存郵件模板的地址和郵件的標題
private static final String[] EmailTemplate = {"static/mail_register_template.html", "static/mail_forget_password_template.html"};
private static final String[] EmailTitle = {"【TIM掙閒錢】註冊郵箱驗證","【TIM掙閒錢】找回密碼郵箱驗證"};
對於郵件的標題,可以很簡單的調用(type
控制是哪種類型郵件)
request.setSubject(EmailTitle[type]);
對於郵件內容,需要讀入html
文件,下面代碼最後的htmlBody
就是模板完整的內容了
這裏之前我沒有用
scanner.nextLine()
而是scanner.next()
最後得到的郵件內容沒有換行…沒有成功解析爲
html
,所以格式極亂
ClassPathResource mailTemplate = new ClassPathResource(EmailTemplate[type]);
System.out.println(mailTemplate);
Scanner scanner = new Scanner(mailTemplate.getInputStream());
String htmlBody = "";
while (scanner.hasNextLine()){
htmlBody += scanner.nextLine() + System.getProperty("line.separator");
}
這樣還不夠,還要給裏面的變量賦值,這就簡單了,字符串替換就好了
htmlBody = htmlBody.replace("[address]", address).replace("[code]", code);
request.setHtmlBody(htmlBody);
這樣就可以很方便地發送通知郵件了,只要幾個參數就行了!
密鑰安全保障
這個在阿里雲短信服務裏已經提到過
所以直接把內容複製過來了,操作是一樣的,就是把郵箱賬號的密鑰保存到文件裏再讀取
如果是開源項目的話,這樣子會直接把密鑰暴露在互聯網中,相當不安全,所以需要做一點措施,把密鑰和代碼分開
在resources
包裏添加包privateKey
在這個包裏新建SMSKey.txt
存儲密鑰
寫上兩行數據,一行是ID
一行是secret
修改代碼,添加兩個私有變量accessKeyId
和secret
,並且在靜態代碼塊裏把它們賦值爲txt
中的值
private static final String accessKeyId;
private static final String secret;
/**
* 初始化獲取私鑰
* */
static {
ClassPathResource smsKey = new ClassPathResource("privateKey/SMSKey.txt");
System.out.println(smsKey);
Scanner scanner = null;
try {
scanner = new Scanner(smsKey.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
accessKeyId = scanner.nextLine();
secret = scanner.nextLine();
scanner.close();
}
然後調用的時候使用這兩個變量就好了
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, secret);
還有一步,不能把這個txt
文件也傳到git
倉庫中去
在gitignore
中添加
SMSKey.txt
現在就可以安心地把代碼push
上去了。
另外這只是開源代碼遇到的問題,如果真的部署到了服務器,還會有以下問題值得思考
-
獲取驗證碼的網站頁面,沒有做防止非法獲取驗證碼的措施,比如最簡單的圖形校驗碼。(常見於網站上獲取驗證碼)
建議:點擊獲取驗證碼之前做一個校驗,比如需要正確輸入圖形驗證碼、拖動驗證等
-
發送短信的接口暴露,且沒有做加密措施,遭受惡意調用。(此類常見於APP側獲取驗證碼)
建議:在短信接口做一些加密措施(例如加入圖形驗證等方式),防止非法調用。