项目优化方案之---实现邮箱用户登录

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/675694.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】链式二叉树(超详细)

文章目录 前言二叉树的链式结构二叉树的遍历方式二叉树的深度优先遍历前序遍历(先根遍历)中序遍历(中根遍历)后序遍历(后根遍历) 二叉树的广度优先遍历层序遍历 二叉树链式结构接口实现二叉树结点个数二叉树叶子结点个数二叉树的深度&#xff08;高度&#xff09;二叉树第k层结…

植物大战僵尸杂交版下载链接

前言 植物大战僵尸杂交版是 潜艇伟伟迷 制作并免费向大家开放畅玩并且持续更新关卡。 下载教程 1.打开作者主页&#xff1a;https://space.bilibili.com/97213827/dynamic 2.作者置顶发布的是最新版&#xff0c;直接打开链接安装就好了 3.下载链接&#xff1a;https://pan.qu…

DL-33G电流继电器 新型导轨安装 JOSEF约瑟

用途 DL-30系列电流继电器&#xff0c;用于电机、变压器和输电线的过负荷和短路保护线路中&#xff0c;作为起动元件。 技术参数 按整定值的范围来分:每整定值的动作误差不大于6% 继电器刻度极限误差不大于6%。 动作值的变差不大于6% 对于DL-31、32、33、34电流继电器的返…

【第3章】SpringBoot实战篇之登录接口(含JWT和拦截器)

文章目录 前言一、JWT1. 什么是JWT2. 使用场景3. 结构3.1 Header3.2 Payload3.3 Signature 4. 使用 二、案例1.引入库2.JwtUtils3. UserController14. ArticleController 三、拦截器1. 定义拦截器2. 注册拦截器 四、测试1. 登录2. 无token3. 有token4. 全局配置 总结 前言 前面…

vscode怎么点击路径直接跳转对应文件

在vue项目中经常要引入工具类、组件、模版等&#xff0c;想要直接去看对应文件&#xff0c;只能自己找到对应路径再去打开。 我们可用在js项目中创建一个 jsconfig.json文件&#xff0c;TS项目可以创建tsconfig.json 文件代码 {"compilerOptions": {"baseUrl&…

批量归一化(BN)和层归一化(LN)的区别

批量归一化&#xff08;Batch Normalization, BN&#xff09;和层归一化&#xff08;Layer Normalization, LN&#xff09;是深度学习中常用的两种归一化技术&#xff0c;它们主要用于解决训练过程中的内部协变量偏移问题&#xff0c;加速模型收敛和提高稳定性。 1. 为什么需要…

智能工厂生产设备实时监控技术的UI设计

智能工厂生产设备实时监控技术的UI设计

Java面试八股之死锁和饥饿的区别

死锁和饥饿的区别 定义与现象&#xff1a; 死锁&#xff08;Deadlock&#xff09;是指两个或多个线程互相等待对方持有的资源而无法继续执行的情况。每个线程至少持有一个资源&#xff0c;并尝试获取另一个由其他线程持有的资源&#xff0c;从而形成一个循环等待的僵局&#…

QAnything-1.4.01.4.1版本更新!使用指北!

久等了各位&#xff01;时隔一个多月&#xff0c;我们在4月26日和5月20日接连发布了v1.4.0和v1.4.1两个版本&#xff0c;带来了问答性能&#xff0c;解析效果等多方面的改进&#xff0c;并新增了大量的新功能和新特性 详见&#xff1a;releases 以及 使用说明 最新特性表 开发…

Android 调试桥_ADB命令

Android 调试桥 ADB全称 【Android Debug Bridge】 是Android SDK中的一个命令行工具&#xff0c;adb命令可以直接操作管理Android模拟器或真实的Android设备&#xff08;手机&#xff09; ADB的工作原理 启动一个 adb 客户端时&#xff0c;此客户端首先检查是否有已运行的 …

1961. 检查字符串是否为数组前缀 - 力扣

1. 题目 给你一个字符串 s 和一个字符串数组 words &#xff0c;请你判断 s 是否为 words 的 前缀字符串 。 字符串 s 要成为 words 的 前缀字符串 &#xff0c;需要满足&#xff1a;s 可以由 words 中的前 k&#xff08;k 为 正数 &#xff09;个字符串按顺序相连得到&#xf…

kaggle竞赛系列基于图像对水稻分类代码案例

目录 依赖环境 代码 导入依赖包 定义数据集路径&#xff1a; 创建训练集、验证集和测试集的文件夹&#xff1a; 代码的作用&#xff1a; 设置新的数据集路径与类别名称 代码的作用&#xff1a; 定义数据预处理和增强变换&#xff1a; 代码的作用&#xff1a; 定义数…

Appium自动化环境搭建保姆级教程

APP自动化测试运行环境比较复杂&#xff0c;稍微不注意安装就会失败。我见过不少朋友&#xff0c;装了1个星期&#xff0c;Appium 的运行环境还没有搭好的。 搭建环境本身不是一个有难度的工作&#xff0c;但是 Appium 安装过程中确实存在不少隐藏的比较深的坑&#xff0c;如果…

kafka-集群搭建(在docker中搭建)

文章目录 1、kafka集群搭建1.1、下载镜像文件1.2、创建zookeeper容器并运行1.3、创建3个kafka容器并运行1.3.1、9095端口1.3.2、9096端口1.3.3、9097端口 1.4、重启kafka-eagle1.5、查看 efak1.5.1、查看 brokers1.5.2、查看 zookeeper 1、kafka集群搭建 1.1、下载镜像文件 d…

Vuforia AR篇(五)— 地平面检测

目录 前言一、什么是地平面识别&#xff1f;二、使用步骤三、示例代码四、效果五、总结 前言 在增强现实&#xff08;AR&#xff09;应用程序的开发中&#xff0c;地平面识别是一项关键技术&#xff0c;它允许虚拟对象与现实世界的地面进行互动。Vuforia 是一个功能强大的 AR …

javacv ffmpeg使用笔记 (补充中...)

javacv ffmpeg使用笔记 一、maven依赖二、示例代码1. 获取视频时长 三、小技巧 一、maven依赖 使用javacv ffmpeg并指定classifier之后&#xff0c;就不需要额外安装ffmpeg软件&#xff08;jar包中已经内置&#xff09;了。 全量依赖包&#xff08;不推荐&#xff09;安装包总大…

6、架构-服务端缓存

为系统引入缓存之前&#xff0c;第一件事情是确认系统是否真的需要缓 存。从开发角度来说&#xff0c;引入缓存会提 高系统复杂度&#xff0c;因为你要考虑缓存的失效、更新、一致性等问题&#xff1b;从运维角度来说&#xff0c;缓存会掩盖一些缺 陷&#xff0c;让问题在更久的…

HashMap的get和put方法

在 JDK 1.8 中&#xff0c;HashMap 是一个常用的实现了 Map 接口的哈希表&#xff0c;它允许存储键值对&#xff0c;并且键和值都可以为 null。HashMap 的主要特点是其基于哈希表的实现&#xff0c;提供了快速的查找和插入操作。以下是 HashMap 中 get 和 put 方法的介绍及其实…

Flink状态State | 大数据技术

⭐简单说两句⭐ ✨ 正在努力的小叮当~ &#x1f496; 超级爱分享&#xff0c;分享各种有趣干货&#xff01; &#x1f469;‍&#x1f4bb; 提供&#xff1a;模拟面试 | 简历诊断 | 独家简历模板 &#x1f308; 感谢关注&#xff0c;关注了你就是我的超级粉丝啦&#xff01; &a…

使用J-LINK COMMANDER检查极海APM32F072烧录

键入 connect: 此时会显示默认设备&#xff0c;如果之前设置过会有&#xff0c;为了演示&#xff0c;我不选 键入 &#xff1f; 然后会弹出设备选择界面&#xff1a; 根据自己的设备搜索型号&#xff1a; 我这里搜索“APM32F072VB”,点击OK: 选择接口类型&#xff1a; 如果要…