【SpringBoot】SpringBoot使用mail实现登录邮箱验证
创始人
2024-12-26 02:11:07
0

427ef4152dbf4b6c92618a198935cb6c.png

  📝个人主页:哈__

期待您的关注 

1b7335aca73b41609b7f05d1d366f476.gif

目录

一、前期准备

1 开启邮箱服务

2 SpringBoot导入依赖

3 创建application.yml配置文件 

4 创建数据库文件

 5 配置redis服务

二、验证邮件发送功能 

三、注册功能实现邮箱验证

1 创建User实体类

2 创建UserParam

3 创建CaptchaService

4 创建EmailTemplateEnum

5 创建CaptchaServiceImpl

 6 创建CaptchaController

7 创建LoginController 

四、创建登陆页面 

1 login.html 

2 register.html


 

在实际的开发当中,不少的场景中需要我们使用更加安全的认证方式,同时也为了防止一些用户恶意注册,我们可能会需要用户使用一些可以证明个人身份的注册方式,如短信验证、邮箱验证等。

一、前期准备

为了实现邮箱认证服务,我们需要提供出来一个邮箱作为验证码的发送者,这里我使用的是QQ邮箱。

1 开启邮箱服务

首先打开QQ邮箱,然后找到设置,点击账号。

a5b0567d19f143a7acbdfa6c60ff09f0.png

然后找到下方的邮件协议服务,打开。因为这里我已经打开了,而且服务开启也较为简单,需要我们发送一个短信到指定的号码,可能会收取一定的费用,不过不是很多,好像是几毛钱。

31d8524967e54c389103fe92393c8b86.png 开启之后会给你一个授权码,一定要记住这个授权码,发邮件的时候需要这个。

2 SpringBoot导入依赖

核心的就是mail依赖,因为我这个项目东西不少,为了方便我就全拷贝过来了,可能有的用不到。

          1.8         UTF-8         UTF-8         2.7.6         UTF-8         UTF-8         1.8         3.4.3.1         3.3.2         1.7                                org.springframework.boot             spring-boot-starter-web                                org.springframework.boot             spring-boot-starter-mail                               org.slf4j             slf4j-log4j12                               org.apache.commons             commons-lang3                               com.mysql             mysql-connector-j                               org.projectlombok             lombok             true                               org.springframework.boot             spring-boot-starter-test             test                               com.baomidou             mybatis-plus-boot-starter             ${mybatisplus.version}                               com.baomidou             mybatis-plus-generator             ${mybatisplus.generator}                               org.apache.velocity             velocity             ${mybatisplus.velocity}                               org.springframework.boot             spring-boot-devtools                               org.springframework.boot             spring-boot-starter-data-redis                               com.alibaba             fastjson             1.2.56                

3 创建application.yml配置文件 

spring:   #邮件服务配置   mail:     host: smtp.qq.com #邮件服务器地址     protocol: smtp #协议     username:  #发送邮件的邮箱也就是你开通服务的邮箱     password:  #开通服务后得到的授权码     default-encoding: utf-8 #邮件内容的编码   redis:     host: 127.0.0.1     port: 6379   datasource:     driver-class-name: com.mysql.cj.jdbc.Driver     url: jdbc:mysql://localhost:3306/mail     username: root     password:  # 数据库的密码

4 创建数据库文件

在上边的配置文件中你也看到了,我们用到了mysql还有redis。

DROP TABLE IF EXISTS `user`; CREATE TABLE `user`  (   `id` int(11) NOT NULL AUTO_INCREMENT,   `account` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,   `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,   PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact;  -- ---------------------------- -- Records of user -- ---------------------------- INSERT INTO `user` VALUES (1, 'admin', '123456'); INSERT INTO `user` VALUES (2, '123456', '123456');  SET FOREIGN_KEY_CHECKS = 1;

 5 配置redis服务

在之前的文章当中我有说到过安装redis的事情,大家可以看看我这篇文章。【Spring】SpringBoot整合Redis,用Redis实现限流(附Redis解压包)_springboot 限流 redis-CSDN博客

二、验证邮件发送功能 

大家可以先看一下我的项目结构。其中的一些代码是我学习另一位大佬的文章,这篇文章也是对该大佬文章的一个总结。蒾酒-CSDN博客

d18a1b016999488794cb4594e46ab934.png

我们最重要的邮件发送工具 就是util包下的EmailApi。将以下代码导入后,创建一个测试方法。

import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.FileSystemResource; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Component;  import javax.annotation.Resource; import javax.mail.internet.MimeMessage; import java.io.File; import java.util.Objects;  /**  * @author mijiupro  */ @Component @Slf4j public class EmailApi {     @Resource     private JavaMailSender mailSender;      @Value("${spring.mail.username}")     private String from ;// 发件人      /**      * 发送纯文本的邮件      * @param to 收件人      * @param subject 主题      * @param content 内容      * @return 是否成功      */     @SneakyThrows(Exception.class)     public boolean sendGeneralEmail(String subject, String content, String... to){         // 创建邮件消息         SimpleMailMessage message = new SimpleMailMessage();         message.setFrom(from);         // 设置收件人         message.setTo(to);         // 设置邮件主题         message.setSubject(subject);         // 设置邮件内容         message.setText(content);          // 发送邮件         mailSender.send(message);          return true;     }     /**      * 发送html的邮件      * @param to 收件人      * @param subject 主题      * @param content 内容      * @return 是否成功      */     @SneakyThrows(Exception.class)     public boolean sendHtmlEmail(String subject, String content, String... to){         // 创建邮件消息         MimeMessage mimeMessage = mailSender.createMimeMessage();         MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);         helper.setFrom(from);         // 设置收件人         helper.setTo(to);         // 设置邮件主题         helper.setSubject(subject);         // 设置邮件内容         helper.setText(content, true);          // 发送邮件         mailSender.send(mimeMessage);          log.info("发送邮件成功");         return true;      }     /**      * 发送带附件的邮件      * @param to 收件人      * @param subject 主题      * @param content 内容      * @param filePaths 附件路径      * @return 是否成功      */     @SneakyThrows(Exception.class)     public boolean sendAttachmentsEmail(String subject, String content, String[] to, String[] filePaths) {         // 创建邮件消息         MimeMessage mimeMessage = mailSender.createMimeMessage();         MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);         helper.setFrom(from);         // 设置收件人         helper.setTo(to);         // 设置邮件主题         helper.setSubject(subject);         // 设置邮件内容         helper.setText(content,true);          // 添加附件         if (filePaths != null) {             for (String filePath : filePaths) {                 FileSystemResource file = new FileSystemResource(new File(filePath));                 helper.addAttachment(Objects.requireNonNull(file.getFilename()), file);              }         }         // 发送邮件         mailSender.send(mimeMessage);         return true;     }      /**      * 发送带静态资源的邮件      * @param to 收件人      * @param subject 主题      * @param content 内容      * @param rscPath 静态资源路径      * @param rscId 静态资源id      * @return 是否成功      */     @SneakyThrows(Exception.class)     public boolean sendInlineResourceEmail(String subject, String content, String to, String rscPath, String rscId) {         // 创建邮件消息         MimeMessage mimeMessage = mailSender.createMimeMessage();         MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);         // 设置发件人         helper.setFrom(from);         // 设置收件人         helper.setTo(to);         // 设置邮件主题         helper.setSubject(subject);          //html内容图片         String contentHtml = "这是邮件的内容,包含一个图片:"+content+"";          helper.setText(contentHtml, true);         //指定讲资源地址         FileSystemResource res = new FileSystemResource(new File(rscPath));         helper.addInline(rscId, res);          mailSender.send(mimeMessage);         return true;     }  }

 进行邮件发送的测试。

cce23808aa9c476eb51c0896c3ef38fa.png

 看看结果,成功的发过来了。

e582910586124e76b8b1baf429574f72.png

 接下来就要进行登录注册功能的开发了。

三、注册功能实现邮箱验证

1 创建User实体类

@Data @EqualsAndHashCode(callSuper = false) public class User implements Serializable {      private static final long serialVersionUID = 1L;      @TableId(value = "id", type = IdType.AUTO)     private Integer id;      private String account;      private String password;   }

2 创建UserParam

@Data @EqualsAndHashCode(callSuper = false) public class UserParam implements Serializable {      private static final long serialVersionUID = 1L;      @TableId(value = "id", type = IdType.AUTO)     private Integer id;      private String account;      private String password;      private String emailCode;      private String email;   } 

3 创建CaptchaService

public interface CaptchaService {     boolean sendCaptcha(String email); }

4 创建EmailTemplateEnum

这一步我没有选择创建CaptchaServiceImpl,因为这个类中涉及到了一些核心的代码,而我们一些类还没有创建完,我们先创建这样的一个枚举类,这个枚举类的作用就是定义我们发送邮件的一个模板,我们在发送邮件的时候,直接向模板内插入内容就可以了。

ublic enum EmailTemplateEnum {     // 验证码邮件     VERIFICATION_CODE_EMAIL_HTML("用户你好,你的验证码是:

%s

请在五分钟内完成注册","登录验证"), // 用户被封禁邮件通知 USER_BANNED_EMAIL("用户你好,你已经被管理员封禁,封禁原因:%s", "封禁通知"); private final String template; private final String subject; EmailTemplateEnum(String template, String subject) { this.template = template; this.subject = subject; } public String getTemplate(){ return this.template; } public String set(String captcha) { return String.format(this.template, captcha); } public String getSubject() { return this.subject; } }

5 创建CaptchaServiceImpl

首先把我们前端传过来的邮箱加一个前缀,用于redis中的存储,因为我们不仅可以有邮箱认证还可以有手机认证。

 @Resource     StringRedisTemplate stringRedisTemplate;     @Resource     EmailApi emailApi;     @Override     public boolean sendCaptcha(String email) {         sendMailCaptcha("login:email:captcha:"+email);         return true;     }

 在redis当中,验证码的存储使用的是Hash结构,Hash存储了验证码,验证次数,还有上一次的发送时间,因为我们要限制一分钟发送的次数。一分钟内我们只能发一条短信,验证码在redis中的过期时间为五分钟,在验证码未过期之前发送的认证,都会让这个发送次数加一,倘若发送的次数达到了5次还要发送,那么就封禁一天不让发送短信。

例如,在3:30:30的时候发送了一次短信,一分钟后,3:31:30的时候又发送了短信,直到3:35:30的时候又发了一次,此时的发送次数已经达到了5,这时候就会封一天,因为每次发送验证码的时候,redis都存储着上一次还没过期的验证码,所以发送次数会增加。

下边讲一下代码。

先从redis中找到这个hash结构,如果hash结构的值不为空并且达到了发送次数上限,就封禁一天,否则的话看一下上一次的发送时间是否存在,如果存在的话,判断一下当前时间和上一次的发送时间间隔是否大于60秒,如果小于60秒那么不让发送。如果正常发送短信,那么就把发送的次数加1,然后用随机数生成一个六位数的验证码,发送验证码,并且向redis中保存刚才的hash结构。

  private boolean sendMailCaptcha(String key){         BoundHashOperations hashOps = stringRedisTemplate.boundHashOps(key);         // 初始检查         String lastSendTimestamp = hashOps.get("lastSendTimestamp");         String sendCount = hashOps.get("sendCount");          if(StringUtils.isNotBlank(sendCount)&&Integer.parseInt(sendCount)>=5){             hashOps.expire(24, TimeUnit.HOURS);             throw new  RuntimeException("验证码发送过于频繁");         }         if(StringUtils.isNotBlank(lastSendTimestamp)){             long lastSendTime = Long.parseLong(lastSendTimestamp);             long currentTime = System.currentTimeMillis();             long elapsedTime = currentTime - lastSendTime;             if(elapsedTime < 60 * 1000){                 throw new  RuntimeException("验证码发送过于频繁");             }         }         int newSendCount = StringUtils.isNotBlank(sendCount) ? Integer.parseInt(sendCount) + 1 : 1;         String captcha = RandomStringUtils.randomNumeric(6);          try {             sendCaptcha(key,captcha);         } catch (Exception e) {             return false;         }         hashOps.put("captcha", captcha);         hashOps.put("lastSendTimestamp", String.valueOf(System.currentTimeMillis()));         hashOps.put("sendCount", String.valueOf(newSendCount));         hashOps.expire(5, TimeUnit.MINUTES); // 设置过期时间为5分钟          return true;     }

发送验证码调用的是下边的函数。

 private void sendCaptcha(String hashKey, String captcha) thorw Exception{         // 根据hashKey判断是发送邮件还是短信,然后调用相应的发送方法         if("email".equals(hashKey.split(":")[1])){             if (!emailApi.sendHtmlEmail(EmailTemplateEnum.VERIFICATION_CODE_EMAIL_HTML.getSubject(),                     EmailTemplateEnum.VERIFICATION_CODE_EMAIL_HTML.set(captcha),hashKey.split(":")[3])) {                 throw new RuntimeException("发送邮件失败");             }         }     }

 6 创建CaptchaController

@RestController @RequestMapping("/captcha") public class CaptchaController {      @Resource     CaptchaService captchaService;     @Resource     EmailApi emailApi;     @RequestMapping("/getCaptcha")     public Result sendCaptcha(String email){         boolean res = captchaService.sendCaptcha(email);         if(res){             return new Result("发送成功",200,null);         }         return new Result("发送失败",500,null);     } }

7 创建LoginController 

UserSevice的东西都很简单,都是mybatisplus的内容,如果不太了解可以看我这篇文章【Spring】SpringBoot整合MybatisPlus的基本应用_简单的springboot+mybatisplus的应用程序-CSDN博客

我这里并没有用UserService封装认证的过程,直接写到controller中了,大家能看懂就好。仅供学习使用。 

@Controller public class LoginController {     @Autowired     UserService userService;     @Autowired     StringRedisTemplate stringRedisTemplate;     @GetMapping("/")      public String Login(){         return "redirect:/pages/login.html";     }      @RequestMapping("/register")     @ResponseBody     public Result registerUser( UserParam user ){         String email = user.getEmail();         String emailCode = user.getEmailCode();         BoundHashOperations hashOps = stringRedisTemplate.boundHashOps("login:email:captcha:"+email);         String code = hashOps.get("captcha");         if(!Objects.equals(code, emailCode)){             return new Result("验证码错误",400,null);         }         User user1 = userService.getOne(new LambdaQueryWrapper()                 .eq(User::getAccount,user.getAccount()));         //如果有这个用户的信息要拒绝注册         if(user1!=null){             return new Result("",100,null);         }         User saveUser = new User();         BeanUtils.copyProperties(user,saveUser);         System.out.println(user);         System.out.println(saveUser);         boolean save = userService.save(saveUser);         if(!save){             return new Result("注册失败",300,null);         }         return new Result("注册成功",200,null);     } }

到此为止,验证码的注册功能就已经实现完成了。

我们现在要做一个前端页面。

四、创建登陆页面 

在resources目录下创建static文件夹。在pages目录下添加login.html和register.html。至于jquery呢就要大家自己去找了。

6a71f75baaa54fae8b7c0bd5ab3e8d86.png

 

1 login.html 

                 Document                        
邮箱简易发送系统
账号:

密码:

2 register.html

                 Document                        
邮箱简易发送系统
账号:

密码:

邮箱:



验证码:

2625f728ad30493391e9248fc41e6579.png

 

相关内容

热门资讯

透视演示!we-poker辅助... 透视演示!we-poker辅助器(透视)开挂脚本辅助器(哔哩哔哩)1.we-poker辅助器 选牌创...
透视总结!德州真人透视脚本(透... 透视总结!德州真人透视脚本(透视)开挂透视辅助器(哔哩哔哩)1、德州真人透视脚本模拟器是什么优化,德...
透视方针!wpk免费辅助(透视... 透视方针!wpk免费辅助(透视)开挂透视工具(哔哩哔哩)1、游戏颠覆性的策略玩法,独创攻略技巧玩法,...
透视技法!hhpoker买挂(... 透视技法!hhpoker买挂(透视)开挂脚本app(哔哩哔哩)1、进入游戏-大厅左侧-新手福利-激活...
透视步骤!德普之星透视免费(透... 透视步骤!德普之星透视免费(透视)开挂脚本工具(哔哩哔哩)1、玩家可以在德普之星透视免费透视最简单三...
透视机巧!hhpoker一直输... 透视机巧!hhpoker一直输有挂吗(透视)开挂透视软件(哔哩哔哩)1、下载好hhpoker一直输有...
透视妙招!wepoker买钻石... 您好,wepoker买钻石有用吗这款游戏可以开挂的,确实是有挂的,需要了解加去威信【13670430...
透视窍要!智星德州插件2024... 透视窍要!智星德州插件2024最新版(透视)开挂透视工具(哔哩哔哩)1、下载好智星德州插件2024最...
透视法门!wepoker透视有... 透视法门!wepoker透视有用吗(透视)开挂透视神器(哔哩哔哩)1.wepoker透视有用吗 选牌...
透视窍门!epoker透视底牌... 透视窍门!epoker透视底牌(透视)开挂透视修改器(哔哩哔哩)1、epoker透视底牌免费辅助多个...