之前的項目中我寫的基于SpringBoot和Vue的全棧項目已經實現了基本的用戶接口開發,
不過其代碼的功能單一,而且寫的也是有不少漏洞(基本就像剛接手的代碼*山一樣)
那之后的幾篇文章都來分享一下如何優化項目(每一章都獨立講方案,不必看之前的)
這里來添加一種登錄方法,用郵箱來登錄(實際思路跟手機號登錄是差不多的,而且都是要借助一些第三方的工具實現)
操作之前先來梳理一下實現流程:
1.用戶填寫其郵箱(這里我只用QQ郵箱實現)
2.點擊發送驗證碼按鈕(ok,此處需要發送請求到后端進行一系列操作Todo)
3.獲取到了驗證碼,填寫驗證碼,并點擊登錄按鈕(這里又要調用一次接口檢驗驗證碼是否正確)
總結一下后端要做的接口有哪些:
1.接收qq郵箱并生成驗證碼發送請求
? ? ? ?(具體實現:1.校驗QQ郵箱是否格式正確(這里用SpringValildation即可),2.生成驗證碼,并將其保存到(??),這里其實有很多種方案可供選擇,比如放到session,這里的話我用ThreadUtils操作一下,(后續統一用Redis操作),然后使用第三方工具把驗證碼發送到對應的QQ郵箱就可以了)
2.接收用戶的驗證碼和其QQ郵箱號,進行二次校驗,驗證驗證碼與生成的驗證碼是否相等,如果相等就可以根據QQ郵箱去查找是否有這個人(這里可以拓展一下,如果沒有這個人就可以直接幫他注冊),這里沒有這個人就返回沒有這個用戶并讓其去注冊的消息,如果有這個人就按照正常流程登錄即可,(這里我的登錄用的是JWT令牌,如果登錄成功還要返回令牌給前端)
Ok,開始后端接口開發:
1.定義DTO
先確認一下之前定義的User實體類中要有email屬性啊
@Email@NotEmpty(message = "郵箱不能為空")private String email;
這里在User實體類這里記得要加上email郵箱屬性(這里還有兩個注釋)
我這里用到了SpringValidation進行參數校驗,(也就是這兩個注解),不知道的同志可以看看之前的文章或者上網搜一下。
OK,這里就可以定義一個接收的實體類了,
由于兩個接口一個接收QQ郵箱,一個接收QQ郵箱+驗證碼,因此這一個DTO也就足夠了(如果愿意,也可以用User,不過我這個User定義的屬性似乎有點多,感覺會很麻煩)
package org.example.cetidenet.model.dto.user;import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;public class UserEmailDTO {@Email@NotEmpty(message = "郵箱不能為空")private String email;private String code;public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}@Overridepublic String toString() {return "UserEmailDTO{" +"email='" + email + '\'' +", code='" + code + '\'' +'}';}
}
2.開發發送驗證碼的接口
在Controller類這里我們新定義一個方法
@PostMapping("/submitEmail")public Result<User> submitEmail(@RequestBody @Validated UserEmailDTO userEmailDTO) {return userService.submitEmail(userEmailDTO);}
這里由于已經使用了@Validated來進行參數校驗了,那我們直接返回userService的方法就好,盡量把邏輯處理的函數都放到Service的實現類中。
既然郵箱的格式已經正確了就生成一串隨機數字吧
這里就使用RandomUtil生成
String code = RandomUtil.randomNumbers(6);
這樣就生成了一個6位的隨機數字;
既然萬事俱備,那么就該發送驗證碼了,
3.講解QQ郵箱發送驗證碼:
?
這里我使用JavaMail發送驗證碼,來看看使用方法:
?
步驟1.獲取QQ郵箱授權碼:
點開QQ郵箱,左上角有設置,點擊設置
再點擊賬號
往下翻,點擊開啟服務即可
然后通過驗證即可獲取到授權碼,(記得保存好)
步驟2:在后端工程中引入依賴
<dependency><groupId>javax.mail</groupId><artifactId>mail</artifactId><version>1.4</version>
</dependency>
步驟3:按照代碼填充:
import java.security.Security;
import java.util.Properties;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;public class MailClient {public static void main(String[] args) {try {final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";//配置郵箱信息Properties props = System.getProperties();//郵件服務器props.setProperty("mail.smtp.host", "smtp.qq.com");props.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);props.setProperty("mail.smtp.socketFactory.fallback", "false");//郵件服務器端口props.setProperty("mail.smtp.port", "465");props.setProperty("mail.smtp.socketFactory.port", "465");//鑒權信息props.setProperty("mail.smtp.auth", "true");//建立郵件會話Session session = Session.getDefaultInstance(props, new Authenticator() {//身份認證protected PasswordAuthentication getPasswordAuthentication() {//1.賬戶 授權碼return new PasswordAuthentication("xxxxxxx@qq.com", "xxxx");}});//建立郵件對象MimeMessage message = new MimeMessage(session);//設置郵件的發件人message.setFrom(new InternetAddress("xxxxxxx@qq.com"));//2.設置郵件的收件人message.setRecipients(Message.RecipientType.TO, "xxxxxxx@qq.com");//設置郵件的主題message.setSubject("通過javamail發出!!!");//文本部分message.setContent("文本郵件測試", "text/html;charset=UTF-8");message.saveChanges();//發送郵件Transport.send(message);} catch (Exception e) {e.printStackTrace();}}
}
此處將? ? ? ? ? return new PasswordAuthentication("xxxxxxx@qq.com", "xxxx");
填充為你的賬號和授權碼,
下面的發件人填寫你的郵箱(不要填別人的,不然要報錯的)
收件人的郵箱填Controller類處接收到的代碼即可;
下面來具體實現一下:
這個郵箱發送代碼較長,這邊可以將其封裝為一個工具類或者函數(我感覺這種要長不長,要短不短的封裝成函數比較合適)
那就來展示一下UserServiceImpl的這個方法調用吧;
@Overridepublic Result<User> submitEmail(UserEmailDTO userEmailDTO) {String email = userEmailDTO.getEmail();String code = RandomUtil.randomNumbers(6);sendEmail(email,code);ThreadLocalUtil.set(code);return Result.success();}private static void sendEmail(String email,String code){try {final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";//配置郵箱信息Properties props = System.getProperties();//郵件服務器props.setProperty("mail.smtp.host", "smtp.qq.com");props.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);props.setProperty("mail.smtp.socketFactory.fallback", "false");//郵件服務器端口props.setProperty("mail.smtp.port", "465");props.setProperty("mail.smtp.socketFactory.port", "465");//鑒權信息props.setProperty("mail.smtp.auth", "true");//建立郵件會話Session session = Session.getDefaultInstance(props, new Authenticator() {//身份認證protected PasswordAuthentication getPasswordAuthentication() {//1.賬戶 授權碼return new PasswordAuthentication("1147683258@qq.com", "bsfyppqvhrqqecgj");}});//建立郵件對象MimeMessage message = new MimeMessage(session);//設置郵件的發件人message.setFrom(new InternetAddress("1147683258@qq.com"));//2.設置郵件的收件人message.setRecipients(Message.RecipientType.TO, email);//設置郵件的主題message.setSubject("來自Cetide網的信息");//文本部分message.setContent("收到您的登錄請求發送驗證碼"+code, "text/html;charset=UTF-8");message.saveChanges();//發送郵件Transport.send(message);} catch (Exception e) {e.printStackTrace();}}
Ok,那么這個接口也就開發的差不多了,這里來驗證一下吧,啟動SpringBoot項目并打開Swagger試試
輸入一個QQ小號的地址即可發送出驗證碼
那么,這也就發送成功了。(這里我測試的時候,感覺請求發出過程好慢,可以看看有沒有更快速一些的QQ郵箱調用)
后續的登錄就比較簡單了,這里添加一個登錄接口
這邊也就不細致講解了,直接上代碼,大家觀看吧
@PostMapping("/email/submit")public Result login(@RequestBody @Validated UserEmailDTO userEmailDTO){// 實現登錄功能return userService.emailLogin(userEmailDTO);}
@Overridepublic Result emailLogin(UserEmailDTO userEmailDTO) {String email = userEmailDTO.getEmail();String userCode = userEmailDTO.getCode();String code = ThreadLocalUtil.get();if(!code.equals(userCode)){return Result.error("驗證碼錯誤");}//現在說明這個驗證碼正確User user = userMapper.findByEmail(email);if(user == null){return Result.error("該郵箱未注冊用戶,請先注冊");}//存在該用戶Map<String, Object> claims = new HashMap<>();claims.put("id", user.getId());claims.put("username", user.getUserName());String token = JwtUtil.genToken(claims);//存在且密碼正確return Result.success(token);}
@Select("select * from user where email = #{email}")User findByEmail(String email);
此處代碼也就差不多能夠實現根據QQ郵箱登錄的功能了;