之前的项目中我写的基于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;
}
@Override
public 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的这个方法调用吧;
@Override
public 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);
}
@Override
public 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邮箱登录的功能了;