之前的项目中我写的基于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邮箱登录的功能了;