Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
率講郵箱驗(yàn)證功能應(yīng)該大多數(shù)人都使用過(guò),比如:對(duì)用戶賬號(hào)密碼進(jìn)行找回,注冊(cè)某網(wǎng)站以郵箱作為登錄名激活賬號(hào).....等多個(gè)應(yīng)用場(chǎng)景都需要我們通過(guò)郵件進(jìn)行驗(yàn)證。此篇教程就是講解如何用Java實(shí)現(xiàn)郵箱驗(yàn)證功能,10分鐘就搞定了,簡(jiǎn)單得一匹!
在日常生活中,我們?cè)谝粋€(gè)網(wǎng)站中注冊(cè)一個(gè)賬戶時(shí),往往在提交個(gè)人信息后,網(wǎng)站還要求我們通過(guò)手機(jī)或郵件來(lái)驗(yàn)證,郵件的話大概會(huì)是下面這個(gè)樣子的:
用戶通過(guò)點(diǎn)擊鏈接從而完成注冊(cè),然后才能登錄。也許你會(huì)想,為什么要這么麻煩直接提交注冊(cè)不就行了嗎?這其中很大一部分原因是為了防止惡意注冊(cè)。接下來(lái)讓我們一起來(lái)使用最簡(jiǎn)單的JSP+Servlet的方式來(lái)完成一個(gè)通過(guò)郵箱驗(yàn)證注冊(cè)的小案例吧。
1、了解相關(guān)協(xié)議:動(dòng)手實(shí)踐之前,你最好對(duì)JSP、Servlet、SMTP協(xié)議和POP3協(xié)議知識(shí)有所了解,如果對(duì)郵件收發(fā)過(guò)程完全不了解的話,可以花點(diǎn)時(shí)間仔細(xì)看一下如下圖:
2、郵箱授權(quán)準(zhǔn)備:在了解的上述內(nèi)容之后,要實(shí)現(xiàn)這個(gè)案例,首先我們還得有兩個(gè)郵箱賬號(hào),一個(gè)用來(lái)發(fā)送郵件,一個(gè)用來(lái)接收郵件。本案例使用QQ郵箱向163郵箱發(fā)送激活郵件,因此需要登錄QQ郵箱,在設(shè)置->賬戶面板中開(kāi)啟POP3/SMTP服務(wù),以允許我們通過(guò)第三方客戶端發(fā)送郵件:進(jìn)入發(fā)件人郵件空間 -> 設(shè)置 -> 賬戶 –> 開(kāi)啟(POP3/SMT)服務(wù) 注意:僅限QQ郵箱
圖一
圖二
圖三
還要注意的是,登錄以下服務(wù): POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服務(wù)時(shí),需要用到授權(quán)碼而不是QQ密碼,授權(quán)碼是用于登錄第三方郵件客戶端的專用密碼。因此我們需要獲得授權(quán)碼,以在后面的程序中使用。
3、需要一個(gè)jar包:mail-1.4.7.jar
下載地址:https://mvnrepository.com/artifact/javax.mail/mail/1.4.7
具體步驟如下:
1. 首先創(chuàng)建一個(gè)web工程,并加入相關(guān)依賴包:
mail-1.4.7.jar 下載地址:https://mvnrepository.com/artifact/javax.mail/mail/1.4.7
2. 使用MySQL創(chuàng)建一張簡(jiǎn)單的用戶表:t_user
create table t_user(
id int(11) primary key auto_increment comment '用戶id',
userName varchar(100) not null comment '用戶名',
userEmail varchar(100) not null comment '用戶郵箱',
userPwd varchar(50) not null comment '用戶密碼',
state int(1) not null default 0 comment '用戶激活狀態(tài):0表示未激活,1表示激活',
code varchar(255) not null comment '激活碼'
);
PS:state字段(用來(lái)判斷用戶賬號(hào)是否激活)和code字段(激活碼)
3. 創(chuàng)建一個(gè)注冊(cè)頁(yè)面
使用JSP創(chuàng)建一個(gè)最簡(jiǎn)單的注冊(cè)頁(yè)面(過(guò)于簡(jiǎn)單,代碼省略):
頁(yè)面效果
4. 注冊(cè)業(yè)務(wù)邏輯分析
1、用戶填寫相關(guān)信息,點(diǎn)擊注冊(cè)按鈕
2、系統(tǒng)先將用戶記錄保存到數(shù)據(jù)庫(kù)中,其中用戶狀態(tài)為未激活
3、系統(tǒng)發(fā)送一封郵件并通知用戶去驗(yàn)證
4、用戶登錄郵箱并點(diǎn)擊激活鏈接
5、系統(tǒng)將用戶狀態(tài)更改為已激活并通知用戶注冊(cè)成功
.................搞清楚了整個(gè)流程,實(shí)現(xiàn)起來(lái)應(yīng)該就不難了...................
public class Constants {
public static final int CODE_STATUS_EXPIRED = 400;//驗(yàn)證碼過(guò)期或者沒(méi)有點(diǎn)擊發(fā)送驗(yàn)證碼
public static final String SEND_EMAIL_ACCOUNT = "2921272303@qq.com";//發(fā)件人郵箱賬號(hào)
public static final String SEND_EMAIL_CODE = "arnsutissvofzfdshj";//授權(quán)碼(自己的)
}
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImpl();
@Override
public int addUser(Users users) {
//注冊(cè)生成唯一標(biāo)識(shí)符
String code = UUID.randomUUID().toString().replaceAll("-","");
try {
String newpwd = MD5.getMD5(users.getUser_pwd()).toString();
users.setUser_pwd(newpwd);
users.setCode(code);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
int result = userDao.addUser(users);
if(result > 0){//注冊(cè)成功
//獲取發(fā)送郵件對(duì)象
EmailUtil emailUtil = new EmailUtil(users.getUser_email(),code);
//發(fā)起郵箱驗(yàn)證
new Thread(emailUtil).start();
}
return result;
}
}
package com.cnlm.utils;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.security.GeneralSecurityException;
import java.util.Properties;
import java.util.UUID;
/**
* Created with IntelliJ IDEA.
* User: cnlm
* Date: 2020/09/014 22:30
* Description: JavaMail發(fā)送郵件工具類
* QQ郵箱賬戶為發(fā)送方,首先需要進(jìn)入QQ郵箱,找到左上方的設(shè)置,然后選擇賬戶,找到POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服務(wù),開(kāi)啟pop3,獲得授權(quán)碼
* Version: V1.0
*/
public class EmailUtil implements Runnable{
private String receiveEmail; // 收件人郵箱
private String uuidCode; // uuid唯一激活碼
public EmailUtil(String receiveEmail, String uuidCode) {
this.receiveEmail = receiveEmail;
this.uuidCode = uuidCode; // 生成唯一隨機(jī)碼;
}
@Override
public void run() {
// 1.創(chuàng)建連接對(duì)象javax.mail.Session
// 2.創(chuàng)建郵件對(duì)象 javax.mail.Message
// 3.發(fā)送一封激活郵件
String host = "smtp.qq.com"; // 指定發(fā)送郵件的主機(jī)smtp.qq.com(QQ)|smtp.163.com(網(wǎng)易)
Properties properties = System.getProperties();// 獲取系統(tǒng)屬性
properties.setProperty("mail.smtp.host", host);// 設(shè)置郵件服務(wù)器
properties.setProperty("mail.smtp.auth", "true");// 打開(kāi)認(rèn)證
//QQ郵箱需要下面這段代碼,163郵箱不需要
try {
//QQ郵箱需要下面這段代碼,163郵箱不需要
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
properties.put("mail.smtp.ssl.enable", "true");
properties.put("mail.smtp.ssl.socketFactory", sf);
// 1.獲取默認(rèn)session對(duì)象
Session session = Session.getDefaultInstance(properties, new Authenticator() {
public PasswordAuthentication getPasswordAuthentication() {
// 發(fā)件人郵箱賬號(hào)、授權(quán)碼
return new PasswordAuthentication(Constant.SEND_EMAIL_ACCOUNT, Constant.SEND_EMAIL_CODE);
}
});
// 2.創(chuàng)建郵件對(duì)象
Message message = new MimeMessage(session);
// 3.設(shè)置發(fā)件人
message.setFrom(new InternetAddress(Constant.SEND_EMAIL_ACCOUNT));
// 4.設(shè)置收件人
message.addRecipient(Message.RecipientType.TO, new InternetAddress(receiveEmail));
// 5.設(shè)置郵件主題
message.setSubject("賬號(hào)激活");
String content = "<html><head></head><body>" +
"<h1>這是一封激活郵件,激活請(qǐng)點(diǎn)擊以下鏈接</h1><h3>" +
"<a href='http://localhost:8080/register/checkRegister.do?code="
+ uuidCode + "'>http://localhost:8080/register/checkRegister.do?code=" + uuidCode
+ "</a></h3></body></html>";
message.setContent(content, "text/html;charset=UTF-8");
// 7.發(fā)送郵件
Transport.send(message); // 阻塞方法
System.out.println("郵件成功發(fā)送!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
PS:自己測(cè)試時(shí)需修改賬號(hào)與授權(quán)碼。
完成后,再有用戶提交注冊(cè)信息時(shí),應(yīng)該就能收到驗(yàn)證郵件了。用戶點(diǎn)擊鏈接后,我們要做的工作就是根據(jù)code(利用UUID生成)更改數(shù)據(jù)庫(kù)中相應(yīng)用戶的狀態(tài),然后提示用戶注冊(cè)結(jié)果了。
最后總結(jié):
簡(jiǎn)單介紹了如何使用JavaMail完成了一個(gè)帶郵箱驗(yàn)證的注冊(cè)案例,當(dāng)然在實(shí)際開(kāi)發(fā)中還有許多細(xì)節(jié)要注意,例如對(duì)用戶提交信息的校驗(yàn),密碼進(jìn)行加密等,此處的簡(jiǎn)單案例并未詳盡處理這些細(xì)節(jié)。
學(xué)會(huì)了嗎?誰(shuí)在最需要的時(shí)候輕輕拍著我肩膀,誰(shuí)在最快樂(lè)的時(shí)候愿意和我分享。我是一個(gè)包夜敲代碼,想靠技術(shù)茍且的程序員。如果覺(jué)得有點(diǎn)用的話,請(qǐng)毫不留情地關(guān)注、點(diǎn)贊、轉(zhuǎn)發(fā)。這將是我寫出更多優(yōu)質(zhì)文章的最強(qiáng)動(dòng)力!
碼重置、兩步驗(yàn)證、商業(yè)機(jī)密、私人對(duì)話……電子郵件是大多數(shù)生活和商業(yè)活動(dòng)的中心,因此我們必須確保它是可信和真實(shí)的。
如果你在使用自己的域名郵箱,證明郵箱真實(shí)性的工作就會(huì)轉(zhuǎn)移到你自己的頭上。本指南旨在為你提供所需的相關(guān)信息和實(shí)踐操作,以保證你域名郵箱的真實(shí)性,并減少你受上當(dāng)受騙的風(fēng)險(xiǎn)。
該指南將介紹域名郵箱安全的三個(gè)主要組成部分:用于簽名的DKIM、用于發(fā)件人驗(yàn)證的SPF和用于更嚴(yán)格執(zhí)行其他兩個(gè)部分的DMARC。請(qǐng)確保你對(duì)DNS有基本的了解,并有使用域名郵箱的經(jīng)驗(yàn)。
SPF,全稱“Sender Policy Framework”,即“發(fā)送方策略框架”,是最基本的電子郵件驗(yàn)證技術(shù)之一,是最簡(jiǎn)單、最常見(jiàn)的保護(hù)。通常,服務(wù)提供商會(huì)提供DNS記錄內(nèi)容,您只需在設(shè)置過(guò)程中簡(jiǎn)單地復(fù)制、粘貼即可。
在DNS解析中,SPF的記錄類型為 TXT。基本格式如下:
"v=spf1 include:spf.httpsmail.com -all"
SPF的核心是一個(gè)IP地址列表,被授權(quán)從你的域名發(fā)送郵件。除此之外,還有其它幾種不同的選項(xiàng):
以上面的地址spf.httpsmail.com為例,該網(wǎng)址下的所有A類型的解析記錄通過(guò)include:選項(xiàng)包含在SPF策略之中。
除了ip地址和包含的主機(jī)名之外,還有其它限定符來(lái)作為選項(xiàng)的前綴。
每個(gè)符號(hào)都會(huì)向郵件服務(wù)器推薦一個(gè)不同的策略,默認(rèn)情況下,如果沒(méi)有符號(hào),它被視為等同于“+”,即“通過(guò)”。(其它限定符可見(jiàn):https://datatracker.ietf.org/doc/html/rfc7208#section-4.6.2,主要包括4個(gè):+、-、~和?等,好像“~”用得較多,為“soft-failure”,字面意思是“軟失敗”,接收但是會(huì)標(biāo)記)
在上面的示例中,主要有兩個(gè)機(jī)制選項(xiàng):
SPF是一個(gè)非常簡(jiǎn)單的工具,提供了基本的電子郵件驗(yàn)證(“允許哪些IP可以發(fā)送我的電子郵件”)來(lái)進(jìn)行基本的垃圾郵件過(guò)濾。即使只是單獨(dú)設(shè)置SPF,也會(huì)對(duì)你的郵件成功投遞有很大幫助。
DKIM,全稱“Domain Keys Identified Mail”,即“域名密鑰識(shí)別郵件標(biāo)準(zhǔn)”,另一種安全機(jī)制,通過(guò)使用非對(duì)稱密鑰以加密的方式驗(yàn)證為你域名郵箱發(fā)送郵件的服務(wù)器是否有權(quán)這樣做。配置DKIM后,接收你郵件的服務(wù)器可以在DNS中查找公鑰,并驗(yàn)證郵件是否從你的域合法發(fā)送。
DKIM可防止IP易手或大型服務(wù)提供商在客戶之間共享IP空間。如果你說(shuō)“谷歌IP可以發(fā)送我的電子郵件”,是什么阻止其他人從你的域中欺騙電子郵件并從他們自己的谷歌帳戶發(fā)送?由于這些IP是共享的,所以它仍將通過(guò)SPF檢查,但不會(huì)通過(guò)DKIM。
DKIM有兩個(gè)主要部分:一個(gè)是帶有公鑰的DNS記錄,另一個(gè)是添加到每個(gè)已發(fā)送電子郵件中的header(頭部信息),其中包含加密簽名和有關(guān)如何查找上述DNS記錄的詳細(xì)信息。
DKIM的DNS解析記錄就是一個(gè)普通的TXT記錄類型,但目前主要以CNAME記錄類型為主,通用格式為:
<selector>._domainkey.<domain>
“selector”通常由您的電子郵件服務(wù)提供商設(shè)置,并在您啟用DKIM時(shí)提供給您。一些提供商,如Fastmail和Microsoft 365,甚至提供多個(gè)選擇器供您設(shè)置。例如,對(duì)于谷歌來(lái)說(shuō),它只是一個(gè)“google”:
google._domainkey.example.com
DKIM頭部信息被添加在你所發(fā)送的每一封郵件中,包含的內(nèi)容很多,但最主要的是以下兩個(gè)部分(其它可見(jiàn)https://datatracker.ietf.org/doc/html/rfc6376#section-3.5):
通過(guò)這兩段元數(shù)據(jù),收件人的服務(wù)器可以重建包含DKIM密鑰的子域并解析它,并根據(jù)此密鑰以驗(yàn)證DKIM簽名和消息是否真實(shí)。
DKIM是一個(gè)比SPF強(qiáng)得多的檢測(cè)垃圾郵件的方法,因?yàn)樗婕暗綄?shí)際的數(shù)學(xué)運(yùn)算,而不僅僅是一個(gè)IP列表。即使你的域名郵箱只配置了SPF和DKIM也已經(jīng)非常不錯(cuò)了,但還可以更進(jìn)一步。
DKIM僅在郵件中有頭部信息時(shí)才適用。這意味著非法消息將不會(huì)有頭部信息,因此不會(huì)發(fā)生DKIM驗(yàn)證。這導(dǎo)致DKIM驗(yàn)證是“中性”的,而不是“失敗”,因?yàn)樗缓?jiǎn)單地忽略了。
添加DMARC策略使我們能夠:
DMARC記錄與其他兩個(gè)記錄的格式相同,而且非常簡(jiǎn)單。下面是一個(gè)非常基本的示例:
v=DMARC1; p=quarantine; adkim=s; aspf=s; rua=mailto: test@dmarc.postmarkapp.com;
DMARC是現(xiàn)代電子郵件安全性中最強(qiáng)大的一部分,它的報(bào)告可以令人難以置信地洞察哪些垃圾郵件偽裝成你。如果您從本指南中得到了什么,我希望您應(yīng)該花時(shí)間和精力制定嚴(yán)格的DMARC政策。
如果你的域名不想發(fā)送郵件,可以通過(guò)以下設(shè)置來(lái)防止垃圾郵件以你的名義發(fā)送出去。
本文翻譯自:Email Authenticity 101: DKIM, DMARC, and SPF,部分內(nèi)容有改動(dòng),已獲得原作者授權(quán)。
現(xiàn)代web開(kāi)發(fā)中,表單是用戶與網(wǎng)站互動(dòng)的重要方式之一。HTML5為表單提交提供了強(qiáng)大的功能和豐富的輸入類型,讓收集和驗(yàn)證用戶輸入數(shù)據(jù)變得更加容易和安全。本文將詳細(xì)介紹HTML5表單的各個(gè)方面,包括基本結(jié)構(gòu)、輸入類型、驗(yàn)證方法和提交過(guò)程。
HTML表單由<form>標(biāo)簽定義,它可以包含輸入字段、標(biāo)簽、按鈕等元素。一個(gè)基本的表單結(jié)構(gòu)如下所示:
<form action="/submit_form" method="post">
<label for="name">姓名:</label>
<input type="text" id="name" name="name" required>
<label for="email">電子郵箱:</label>
<input type="email" id="email" name="email" required>
<input type="submit" value="提交">
</form>
在這個(gè)例子中,表單有兩個(gè)輸入字段:姓名和電子郵箱。每個(gè)輸入字段都有一個(gè)<label>標(biāo)簽,這不僅有助于用戶理解輸入的內(nèi)容,也有助于屏幕閱讀器等輔助技術(shù)。<form>標(biāo)簽的action屬性定義了數(shù)據(jù)提交到服務(wù)器的URL,method屬性定義了提交數(shù)據(jù)的HTTP方法(通常是post或get)。
HTML5提供了多種輸入類型,以支持不同的數(shù)據(jù)格式和設(shè)備。
<!-- 單行文本 -->
<input type="text" name="username" placeholder="請(qǐng)輸入用戶名" required>
<!-- 密碼 -->
<input type="password" name="password" required minlength="8">
<!-- 郵箱 -->
<input type="email" name="email" required placeholder="example@domain.com">
<!-- 搜索框 -->
<input type="search" name="search" placeholder="搜索...">
<!-- 數(shù)值 -->
<input type="number" name="age" min="18" max="100" step="1" required>
<!-- 滑動(dòng)條 -->
<input type="range" name="volume" min="0" max="100" step="1">
<!-- 電話號(hào)碼 -->
<input type="tel" name="phone" pattern="^\+?\d{0,13}" placeholder="+8613800000000">
<!-- 日期 -->
<input type="date" name="birthdate" required>
<!-- 時(shí)間 -->
<input type="time" name="appointmenttime">
<!-- 日期和時(shí)間 -->
<input type="datetime-local" name="appointmentdatetime">
<!-- 復(fù)選框 -->
<label><input type="checkbox" name="interest" value="coding"> 編程</label>
<label><input type="checkbox" name="interest" value="music"> 音樂(lè)</label>
<!-- 單選按鈕 -->
<label><input type="radio" name="gender" value="male" required> 男性</label>
<label><input type="radio" name="gender" value="female"> 女性</label>
<!-- 下拉選擇 -->
<select name="country" required>
<option value="china">中國(guó)</option>
<option value="usa">美國(guó)</option>
</select>
<!-- 顏色選擇器 -->
<input type="color" name="favcolor" value="#ff0000">
<!-- 文件上傳 -->
<input type="file" name="resume" accept=".pdf,.docx" multiple>
HTML5表單提供了內(nèi)置的驗(yàn)證功能,可以在數(shù)據(jù)提交到服務(wù)器之前進(jìn)行檢查。
<input type="text" name="username" required>
<input type="text" name="zipcode" pattern="\d{5}(-\d{4})?" title="請(qǐng)輸入5位數(shù)的郵政編碼">
<input type="number" name="age" min="18" max="99">
<input type="text" name="username" minlength="4" maxlength="8">
當(dāng)用戶填寫完表單并點(diǎn)擊提交按鈕時(shí),瀏覽器會(huì)自動(dòng)檢查所有輸入字段的有效性。如果所有字段都滿足要求,表單數(shù)據(jù)將被發(fā)送到服務(wù)器。否則,瀏覽器會(huì)顯示錯(cuò)誤信息,并阻止表單提交。
<input type="submit" value="提交">
可以使用JavaScript來(lái)自定義驗(yàn)證或處理提交事件:
document.querySelector('form').addEventListener('submit', function(event) {
// 檢查表單數(shù)據(jù)
if (!this.checkValidity()) {
event.preventDefault(); // 阻止表單提交
// 自定義錯(cuò)誤處理
}
// 可以在這里添加額外的邏輯,比如發(fā)送數(shù)據(jù)到服務(wù)器的Ajax請(qǐng)求
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>表單提交并顯示JSON</title>
</head>
<body>
<!-- 表單定義 -->
<form id="myForm">
<label for="name">姓名:</label>
<input type="text" id="name" name="name">
<br>
<label for="email">電子郵件:</label>
<input type="email" id="email" name="email">
<br>
<input type="button" value="提交" onclick="submitForm()">
</form>
<script>
// JavaScript函數(shù),處理表單提交
function submitForm() {
// 獲取表單元素
var form = document.getElementById('myForm');
// 創(chuàng)建一個(gè)FormData對(duì)象
var formData = new FormData(form);
// 創(chuàng)建一個(gè)空對(duì)象來(lái)存儲(chǔ)表單數(shù)據(jù)
var formObject = {};
// 將FormData轉(zhuǎn)換為普通對(duì)象
formData.forEach(function(value, key){
formObject[key] = value;
});
// 將對(duì)象轉(zhuǎn)換為JSON字符串
var jsonString = JSON.stringify(formObject);
// 彈出包含JSON字符串的對(duì)話框
alert(jsonString);
// 阻止表單的默認(rèn)提交行為
return false;
}
</script>
</body>
</html>
在這個(gè)例子中:
注意,這個(gè)例子中我們使用了type="button"而不是type="submit",因?yàn)槲覀儾幌M韱斡心J(rèn)的提交行為。我們的JavaScript函數(shù)submitForm會(huì)處理所有的邏輯,并且通過(guò)返回false來(lái)阻止默認(rèn)的表單提交。如果你想要使用type="submit",你需要在<form>標(biāo)簽上添加一個(gè)onsubmit="return submitForm()"屬性來(lái)代替按鈕上的onclick事件。
HTML5的表單功能為開(kāi)發(fā)者提供了強(qiáng)大的工具,以便創(chuàng)建功能豐富、用戶友好且安全的網(wǎng)站。通過(guò)使用HTML5的輸入類型和驗(yàn)證方法,可以確保用戶輸入的數(shù)據(jù)是有效的,同時(shí)提高用戶體驗(yàn)。隨著技術(shù)的不斷進(jìn)步,HTML5表單和相關(guān)API將繼續(xù)發(fā)展,為前端工程師提供更多的可能性。
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。