卜凡港
摘要
當今社會,電子郵件的應用非常廣泛,例如在某網(wǎng)站注冊了一個賬戶,自動發(fā)送一封歡迎郵件,通過郵件找回密碼,自動批量發(fā)送活動信息等。但這些應用不可能和我們自己平時發(fā)郵件一樣,先打開瀏覽器,登錄郵箱,創(chuàng)建郵件再發(fā)送。為了更好的做好用戶注冊的安全性,保證有效用戶數(shù),以及對產(chǎn)品接下來的相關信息的推廣,本文將首先簡單介紹一些相關的術語和協(xié)議,并通過Java代碼來創(chuàng)建電子郵件,并連接郵件服務器發(fā)送郵件,以及郵件鏈接的驗證,激活注冊用戶的相關功能,并對注冊時產(chǎn)生的一定量高并發(fā)進行研究。該研究對項目開發(fā)者學習和實現(xiàn)服務器端與郵箱服務器之間通信功能有一定的參考價值。
【關鍵詞】JavaMail 電子郵件 Web 高并發(fā)
隨著互聯(lián)網(wǎng)領域各項技術迅速地發(fā)展,電子郵箱已經(jīng)成為現(xiàn)代人常用的一種聯(lián)系工具,并且涉及的領域也越來越廣泛,個人郵箱有時可以看作是一個身份認證的標識,可以用來進行身份確認,以及進行相關的聯(lián)系活動?,F(xiàn)在有很多WEB開發(fā)都需要JavaMail,例如,用戶注冊后,網(wǎng)站發(fā)送一封激活郵件進行驗證;用戶過生日,系統(tǒng)發(fā)送生日祝福郵件;將最新活動和優(yōu)惠以郵件的形式告知會員等等。這些應用都需要開發(fā)人員使用編程發(fā)送郵件。本次設計就是基于JavaMail實現(xiàn)用戶注冊時,通過郵件對賬戶進行認證的完善登陸系統(tǒng)。通過郵箱激活注冊賬戶,既保證了對相關賬戶的安全性,也拓展了信息推廣的途徑,這方式已經(jīng)成為了很多IT公司用戶注冊必經(jīng)步驟。整個設計采用B/S架構,在設計過程中,我們首先搭建并配置郵箱服務器端和郵件客戶端,數(shù)據(jù)庫采用MySQL,在數(shù)據(jù)庫中建立相應數(shù)據(jù)表,導入相應的JavaMail的jar包,在eclipse平臺上引用相應的API,并考慮到一定的高并發(fā)性,采取多線程,用JAVA語言進行開發(fā)郵箱驗證功能。我們在最后的實驗結果檢測過程中,會檢測分析到系統(tǒng)成功實現(xiàn)郵件發(fā)送、接收,以及點擊鏈接后的激活功能,本次系統(tǒng)的設計也將對研究人員在用戶安全性設計方面有一定的幫助。
1 JavaMail
JavaMail是由SUN公司發(fā)布,提供給開發(fā)者處理電子郵件相關的編程接口。JavaMail支持常用的SMTP、POP3、TMAP等郵件協(xié)議,開發(fā)人員使用JavaMail編寫郵件程序時,無需考慮底層的通信細節(jié)(Socket),可以更加有效地實施郵件傳輸操作,同時JavaMail所提供的API也能夠支持創(chuàng)建出各種復雜MIME格式的郵件內(nèi)容。JavaMail的API結構圖如圖1所示。
JavaMail API提供了消息、地址和頭文件等基本的郵件系統(tǒng)對象,即使針對不同的郵件協(xié)議,不同的廠商,都可以從JavaMail所提供的一系列接口中找到相應的實現(xiàn)類,從而滿足開發(fā)者多方面需求。
2 郵件收發(fā)協(xié)議
協(xié)議是用來規(guī)定雙方應該滿足什么樣的格式,回什么樣格式的數(shù)據(jù),才可以進行正常通訊的一個規(guī)定。當我們在郵箱服務器上申請空間,進行郵件的傳輸接收,都是默認了一種通訊協(xié)議,以便通訊的正常進行。如果讓JavaMail與郵件服務器通信,就需要相應的協(xié)議支持,該部分稱為服務提供者接口,也就是JavaMail自身需要的協(xié)議支持。
2.1 SMTP協(xié)議
SMTP,即簡單傳輸協(xié)議,它是一組用于由源地址到目的地址傳送郵件的規(guī)則,控制信件的中轉方式,屬于TCP/IP協(xié)議簇,幫助每臺計算機在發(fā)送或中轉信件時找到下一個目的地。通過SMTP所指定的服務器,可以把E-mail在較短時間內(nèi)寄到收件人的服務器上,通常把處理用戶SMTP請求的郵件服務器稱為SMTP服務器,SMTP服務器則是遵循SMTP協(xié)議的發(fā)送郵件服務器,用來發(fā)送或中轉電子郵件,SMTP服務器的默認端口號為25。
2.2 POP3協(xié)議
POP3,即郵局協(xié)議版本3,主要用于支持使用客戶端遠程管理在服務器上的郵件。如果用戶想從郵箱服務器中獲得或接受一封電子郵件的話,連上郵件服務器后,也需要遵循一定的通訊格式,而POP3協(xié)議就是用來定義這種通訊格式。通常把處理POP3請求的服務器稱為POP3服務器,它的默認端口號為110。
3 郵件驗證方案分析
移動互聯(lián)網(wǎng)迅速發(fā)展,社交聯(lián)系得越密切,社交手段越多樣化,而電子郵箱也是當今社交重要方式之一,所以JavaMail在產(chǎn)品開發(fā)中必不可少,而了解郵箱收發(fā)過程也是很多開發(fā)人員容易忽視的一點。通過對郵件收發(fā)過程,郵件驗證邏輯,以及并發(fā)量的解決方案分析可以使系統(tǒng)設計得更加完善。
3.1 由卜箱收發(fā)過程分析
郵箱收發(fā)過程遵循之前所講解的郵件協(xié)議,并根據(jù)相應協(xié)議所對應的服務器進行通信,具體收發(fā)過程如圖2所示。
假設現(xiàn)在有兩個郵箱,我們想讓163郵箱向126郵箱發(fā)送一封郵件,讓163郵箱賬戶和密碼通過客戶端進行鏈接,客戶端可以是網(wǎng)頁版或者是軟件版,登陸到郵箱服務器上,連接郵箱服務器中的SMTP服務器上,然后進行寫郵件操作,將寫好的郵件同,163郵箱的SMTP服務器連接到126郵箱的SMTP服務器上,把這封郵件發(fā)送到對方郵箱服務器的SMTP上,于是126郵箱將會把這封郵件存儲到其內(nèi)在的存儲空間里面。收郵件過程,用戶通過注冊時的用戶名和密碼,連接到相應郵箱服務器中的POP3服務器上,找到這封郵件,并返回到客戶端,用戶就能看到對方發(fā)送過來的郵件,整個過程反之亦然。
3.2 驗證邏輯分析
在設計過程中,在數(shù)據(jù)表中設置state字段,0為激活,1為非激活狀態(tài),當用戶第一次注冊后,數(shù)據(jù)表中傳入相應字段,state處于非激活狀態(tài),然后發(fā)送一封激活郵件到對應的注冊郵箱,用戶點擊郵件中的激活鏈接后,state由非激活狀態(tài)變?yōu)榧せ顮顟B(tài)。如圖3所示。
郵件驗證的邏輯,相對而言較為簡單,在實現(xiàn)注冊功能時,服務器產(chǎn)生激活碼,并根據(jù)開發(fā)者的配置,給注冊用戶發(fā)送含有激活碼鏈接的激活郵件,通過點擊鏈接,改變state的取值從而實現(xiàn)用戶激活,但如果遇上系統(tǒng)新開放注冊,這將會產(chǎn)生一定量的并發(fā)量,若一定時間段內(nèi),用戶一窩蜂地進行注冊操作,將有可能造成SMTP服務器產(chǎn)生拒絕421等狀態(tài),接下來將對并發(fā)量的解決方案進行分析。
3.3 并發(fā)量分析
由于注冊功能不會像電商秒殺功能一樣,瞬間有上千萬甚至上億的并發(fā)量,所以考慮節(jié)約資源,且可抗住一定量的并發(fā)量,我們可將注冊頁面放入CDN,將發(fā)送功能采用多線程進行封裝,從而達到一定量的抗并發(fā)效果。具體多線程實現(xiàn)邏輯如圖4所示。
本次設計采用ThreadPoolExecutor線程池來實現(xiàn)多線程,并在線程池中設定同時運行的線程數(shù)量,線程池維護線程所允許的空閑時間的單位,以及緩沖隊列,從而達到對線程池的參數(shù)的設置以及優(yōu)化,在參數(shù)設置基礎上,多的任務可以進行排隊,從而可以實現(xiàn)了多線程的JavaMail并發(fā)。
4 Web郵件系統(tǒng)編碼實現(xiàn)
本套系統(tǒng)的實現(xiàn)采用的是MVC框架結構,這是目前相對主流且成熟的框架,我們首先在MySQL數(shù)據(jù)庫中建立數(shù)據(jù)庫和相應的用戶表,配置C3P0數(shù)據(jù)池鏈接,導入所需要的Jar包,根據(jù)MVC結構,依次從實體類,DAO層,再到業(yè)務層,最后編寫注冊Servlet和激活Servlet實現(xiàn)系統(tǒng)設計功能。
4.1 DAO層的實現(xiàn)
用戶注冊,首先調(diào)用數(shù)據(jù)訪問層中定義的方法,通過JDBC將數(shù)據(jù)存入到數(shù)據(jù)庫用戶表中,state狀態(tài)為。值,并隨機生成一串編碼,并通過程序生成含有激活碼的字符串鏈接,然后再給用戶發(fā)送一封含有該激活碼鏈接的郵件,此時通過DAO層與數(shù)據(jù)庫的交互,部分代碼如下:
Connection conn=DBUtil.getConnectionn;
String sql =-insert into user(usemame,email,password,state,code)values(?,?,?,?,?)";
PreparedStatement pstmt=conn.prepareStatement(sql);
pstmt.setString(1,user.getUsemame());
pstmt.setString(2,user.getEmail());
pstmt.setString(3,user.getPasswordo);
pstmt.setlnt(4,user.getState());
pstmt.setString(5,user.getCode());
num=pstmt.executeUpdateo;
DBUtil.close(conn,pstmt,null);
DBUtil是封裝好的數(shù)據(jù)庫連接類,通過編寫一條SQL語句,經(jīng)預編譯進行處理,然后一一對應進行傳值,從而實現(xiàn)與數(shù)據(jù)庫的交互。
4.2 業(yè)務層的實現(xiàn)
根據(jù)WC結構,我們將業(yè)務層首先規(guī)定相應方法的接口,再在業(yè)務層中定義實現(xiàn)類,通過繼承這些接口,實現(xiàn)業(yè)務層中定義的相應方法。在注冊功能方面,利用正則表達式驗證郵箱是否符合郵箱格式,若符合生成激活碼,把相應數(shù)據(jù)存入到數(shù)據(jù)庫中,保存成功后通過線程方式給用戶發(fā)送一封郵件,再通過調(diào)用DAO層中激活用戶的方法實現(xiàn)激活功能,其中部分代碼如下所示:
String code=CodeUtil.generateUniqueCode();
User user=new User(usemame,email,password,0,code);
UserDao userDao=newUserDaolmpl();
if(userDao.save(user)>0){
new Thread(newMailUtil(email,code)).start();
return true;
}
在抗并發(fā)上,整個事務邏輯是通過調(diào)用線程池接口,開啟線程,實現(xiàn)用戶的注冊和激活功能,并在一定程度上,可以抗住一定量的用戶注冊產(chǎn)生的并發(fā)量。
4.3 Servlet的實現(xiàn)
本次設計中,為了實現(xiàn)Web應用程序,不收到cGI程序性能的限制,我們通過采用JavaServlet來實現(xiàn)對數(shù)據(jù)庫中的JDBCAPI進行訪問,本設計涉及到的最主要的兩部分Servlet是實現(xiàn)用戶注冊Servlet和激活Servlet,我們需將其首先繼承 Httpservlet類,并復寫doGet和doPost方法,其中部分代碼如下:
String userName=request.getParameter("usemame");
String password=request.getParameter("password");
String email=request.getParameter("email");
UserService userService=newUserServiceImpl();
if(userService.doRegister(userName,password,email)){
request.setAttribute("msg","注冊成功,請登錄郵箱激活賬號");
}else{
request.setAttribute("msg","注冊失敗,請檢查相關信息")
}
request.getRequestDispatcher("/result.jsp").forward(request,response);
在doPost方法中接收數(shù)據(jù),將數(shù)據(jù)進行封裝,調(diào)用業(yè)務,實現(xiàn)業(yè)務實現(xiàn)類,將用戶信息和激活碼傳遞過去,根據(jù)激活碼查詢用戶,更改用戶狀態(tài),將狀態(tài)0變?yōu)?,同時將激活碼在相應數(shù)據(jù)設置為NULL,然后調(diào)用request的getRequestDispatcher方法進行頁面跳轉。
4.4 JavaMail多線程的實現(xiàn)
創(chuàng)建連接對象javax.mail.session,然后創(chuàng)建郵件對象javax.mail.Message,再發(fā)送一封激活郵件,某些郵箱服務器要求SMTP連接需要使用SSL安全認證,為了提高安全性,郵箱支持SSL連接,也可以自己開啟,如果無法連接郵件服務器,則需仔細查看控制臺打印的日志信息,如果有類似“連接失敗,要求SSL安全連接”等錯誤,可以重新設置SMTP服務器端口,部分代碼如下:
Message message=newMimeMessage(session);
message.setFrom(newIntemetAddress(from));
message.addRecipient(Message.RecipientType.TO,new IntemetAddress(email));
message.setSubject("賬號激活");
String content="
這是一封激活郵件,激活請點擊以下鏈接
";
message.setContent(content,"text/html;charset=UTF-8");
Transport.send(message);
System.out.println("郵件成功發(fā)送!");
先指定發(fā)件人電子郵箱,指定發(fā)送郵件的主機,獲取系統(tǒng)屬性,設置郵件服務器,打開認證,若是QQ郵箱,還需要通過MailSSLSocketFactory獲取相關屬性,當這些步驟完成后,我們將進行最后的發(fā)驗證郵件操作,獲取默認session對象,創(chuàng)建郵件對象,設置發(fā)件人,設置郵件主題,設置郵件內(nèi)容,將要發(fā)送的功能封裝成Runnable,最后進行發(fā)送郵件。
5 測試
本套設計系統(tǒng)采用的是Tomcat服務器進行運行,首先我們在注冊頁面中輸入用戶名,注冊郵箱,和密碼。注冊完后,數(shù)據(jù)庫表中會有相應的用戶信息,state值為0,并我們將在注冊郵箱中收到一封激活郵件,如圖5所示。
當點擊郵件激活碼鏈接后,會進行頁面跳轉,并提示成功進行激活,然后跳轉到登陸頁面進行登陸操作,此時注冊用戶已成為有效用戶,我們可以再觀察數(shù)據(jù)庫用戶表中的相關數(shù)據(jù),如圖6所示。
從圖6中我們首先可以看出注冊時的數(shù)據(jù)已經(jīng)成功注入到數(shù)據(jù)表中,并注意到password密碼是經(jīng)過MD5進行加密處理的,這樣能很大程度上保證的用戶的安全性,另外從后面兩個字段中,并且state狀態(tài)由。變1,激活碼也根據(jù)之前程序設定的自動清空,該賬戶已經(jīng)成功被激活,從而可以進行后面研發(fā)產(chǎn)品中任何體驗操作。
6 結束語
本次驗證系統(tǒng)設計,首先根據(jù)對SMTP和POP3郵件相關協(xié)議的了解,詳細論述了郵件收發(fā)過程和注冊郵件激活邏輯,并通過多線程防止高并發(fā)量造成的死鎖,采用JavaMail提供的API實現(xiàn)郵件發(fā)送和用戶的激活功能。整個系統(tǒng)架構采用JSP+Servlet結構,輕化了系統(tǒng)框架,節(jié)省了資源。在設計過程中,我們也需要注意,并不是所有的提供郵箱的網(wǎng)站都會對用戶開通SMTP服務器,一般可以使用的SMTP服務器都需要身份驗證的,且無法進行匿名發(fā)郵件。從測試結果來說,我們可以成功實現(xiàn)注冊時產(chǎn)生激活碼的鏈接,對用戶發(fā)送含激活鏈接的郵件,用戶點擊郵件中鏈接后,也成功實現(xiàn)激活用戶的操作。但本次設計中,仍存在一定的缺陷,無法承擔企業(yè)級的并發(fā)量,整體功能并不完善,當用程序對一個郵箱發(fā)送大量郵件,也可能會被某些郵箱服務器當作外界攻擊。整體來講,本次設計對JavaMail的運用和對郵件系統(tǒng)的設計有著一定的參考價值。
參考文獻
[1]Bruce Eckel.Thinking in Java[M].Upper Saddle River,New Jersey,USA:Prentice Hall,2006.
[2](美)阿諾德,Ken Arnold等.Java程序設計語言[M].北京:人民郵電出版社,2006.
[3]姜承堯.高性能網(wǎng)站MySQL數(shù)據(jù)庫實踐.維普中文科技期刊數(shù)胡庫,2013.
[4]林學良.JSP&Servlet學習筆記.北京:清華大學出版社,2012.
[5]羅軍舟,黎波濤,楊明.TCP/IP協(xié)議及網(wǎng)絡編程技術[M].北京:清華大學出版社,2004.
[6]何進,謝松巍.基于Socket的TCP/IP網(wǎng)絡通訊模式研究[J].計算機應用研究,2001,18(08):134-135
[7]孫衛(wèi)琴.Tomcat與Java Web開發(fā)技術詳解[M].北京:電子工業(yè)出版社,2009.
[8]賀松平.基于MVC模式的B/S架構的研究及應用[D].武漢:華中科技大學,2006(04).
[9]趙俊峰等.Java Web應用開發(fā)案例教程:基于MVC模式的JSP+ServIet+JDBC和AJAX[M].北京:清華大學出版社,2012(01)
[10]張峋,楊三成.關鍵技術:JSP與JDBC應用詳解[M].中國鐵道出版社,2010(11).