🚀 注重版权,转载请注明原作者和原文链接

🌐 Open开袁 的官方网站已全面升级!探索更多精彩内容,尽在:https://open-yuan.com

在这里,你将发现丰富的编程资源、深度的技术文章,以及开源项目的地址,一起加入我们的技术交流社区吧!

📱 想要随时随地获取最新动态?微信搜索公众号“全栈小袁”,一手掌握最新项目动态和技术分享。让我们一起,开启技术的无限可能!🌈


🚀 花了几个小时做了一个SpringBoot+Vue实现邮箱登录注册找回密码的demo项目,项目已在Gitee上开源,Gitee开源地址(有接口文档):https://gitee.com/yuandewei/Yuan-SpringBoot/tree/master

跟着我的脚本一步一步实现代码,学会了你也能自己写出来 (或者根据接口文档自己写后端)✨

小袁有话说
今天的教程内容呢是实现邮箱注册登录账号,以及发送邮箱验证码校验验证码和找回密码等,效果图如下

文章底部有视频效果展示

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

话不多说,开始今天的详细教程

数据表的设计,没啥问题,邮箱、密码、加密盐,这里只是演示邮箱功能,所以字段就没有设计太多,需要的就自己额外设计了

在这里插入图片描述

🧸 Redis安装启动

redis本次案例用于存储请求权限码和邮箱验证码

复制链接下载windows解压版 https://user.xiaoyuan-boke.com/Redis-x64-5.0.14.zip

下载好直接解压就好,打开cmd命令控制台,来到刚刚解压的位置(我这里是F盘下的redis文件夹)

输入指令启动 redis-server.exe redis.windows.conf,显示下面这样则成功启动,窗口不能关闭,关闭了redis也跟着关闭了

在这里插入图片描述

🎃 创建项目,配置文件

我这里呢是直接创建一个SpringBoot的项目,启动类加上组件扫描映射文件扫描,同时创建基础结构目录,如图

项目如何创建这里就不一步一步教啦,相信之前看过几篇教学应该都会了

在这里插入图片描述

依赖
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.9.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	
	<properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
        <!-- 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- lombok插件 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Redis依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- 处理JSON的 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
        <!-- 工具包 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>
        <!-- MD5加密的依赖包 -->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>
        <!-- 邮箱依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
    </dependencies>
配置文件

application 这个也没啥问题吧,大家应该都能看懂

server:
  port: 8081

spring:
  # 数据源配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql:///user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    username: xiaoyuan
    password: root
  redis:
    port: 6379
    host: localhost
  # 时间格式转换
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
  profiles:
    # 引入application-email配置文件
    include: email

mybatis-plus:
  # mapper文件映射路径
  mapper-locations: classpath*:mapper/*.xml
  configuration:
    # 打印SQL语句
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
  level:
    com.shyroke.mapper: debug

application-email.yml 这个是配置邮箱信息的

spring:
  mail:
    host: smtp.qq.com
    username: 自己的QQ邮箱
    password: 授权码
    protocol: smtp
    default-encoding: UTF-8
    properties:
      mail:
        smtp:
          auth: true
          starttls:
            enable: true
            required: true
          ssl:
            enable: true

🎯 这里主要一点,要实现邮箱发送功能,得开启SMTP服务,打开自己的QQ邮箱,点击设置 -> 点击账户 -> 开启SMTP服务 -> 获取授权码,将授权码复制到上面的 application-email.yml 文件里

在这里插入图片描述

⚽ db

User 实体类,entity 包下

@Data
@TableName("t_user")
public class User {

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    private String email;

    private String password;

    private String salt;
}

UserMapper 数据访问接口,mapper 包下,继承MyBatis-Plus的 BaseMapper,内部封装了单表的大部分操作

@Repository
public interface UserMapper extends BaseMapper<User> {

}

⚾ constant静态变量

constant 包下新建 RedisConstant 类,

public interface RedisConstant {

    // Key
    String EMAIL = "EMAIL_"; // 邮箱缓存前缀
    String EMAIL_REQUEST_VERIFY = "EMAIL_REQUEST_VERIFY_"; // 邮箱请求的权限码

    // 缓存时间
    int EXPIRE_TEN_SECOND = 10; // 10s
    int EXPIRE_ONE_MINUTE = 60; // 1分钟
    int EXPIRE_FIVE_MINUTE = 5 * 60; // (五分钟)
    int EXPIRE_HALF_HOUR = 30 * 60; // 半小时(30分钟)
    int EXPIRE_ONE_DAY = 24 * 60 * 60; // (1天)
}

新建 HttpStatusEnum 枚举类

@Getter
public enum HttpStatusEnum {

    EMAIL_ERROR(4001, "邮箱格式不正确"),
    PARAM_ERROR(4002, "参数格式不正确"),
    CODE_ERROR(4002, "验证码不正确"),
    PASSWORD_ERROR(4003, "密码错误"),
    USER_NOT_EXIST(4004, "用户不存在"),
    EMAIL_ALREADY_EXIST(4005, "邮箱已被注册"),
    PASSWORD_INCONSISTENT(4006, "密码不一致"),
    PARAM_ILLEGAL(4007, "参数不合法"),

    INTERNAL_SERVER_ERROR(500, "服务器异常"),
    UNKNOWN_ERROR(66666, "未知异常, 联系管理员"),
    ILLEGAL_OPERATION(88888, "非法操作");

    private final int code;
    private final String msg;

    HttpStatusEnum(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

🥎vo对象

统一结果返回

vo 包下新建 R 统一返回类

@Data
public class R {

    private Boolean success;

    private Integer code;

    private String message;

    private Map<String, Object> data = new HashMap<>();

    // 把构造方法私有化
    private R() {}

    // 成功静态方法
    public static R ok() {
        R r = new R();
        r.setSuccess(true);
        r.setCode(200);
        r.setMessage("成功");
        return r;
    }

    // 失败静态方法
    public static R error() {
        R r = new R();
        r.setSuccess(false);
        r.setCode(20001);
        r.setMessage("失败");
        return r;
    }

    // 失败静态方法
    public static R error(HttpStatusEnum httpStatus) {
        R r = new R();
        r.setSuccess(false);
        r.setCode(httpStatus.getCode());
        r.setMessage(httpStatus.getMsg());
        return r;
    }

    public R success(Boolean success){
        this.setSuccess(success);
        return this;
    }

    public R message(String message){
        this.setMessage(message);
        return this;
    }

    public R code(Integer code){
        this.setCode(code);
        return this;
    }

    public R data(String key, Object value){
        this.data.put(key, value);
        return this;
    }

    public R data(Map<String, Object> map){
        this.setData(map);
        return this;
    }
}
参数

新建param包,包下新建LoginParam

@Getter
public class LoginParam {
    
    private String email; // 邮箱
    
    private String password; // 密码
    
    private String passwordConfirm; // 确认密码
    
    private String code; // 验证码
}

🏀utils工具类

utils 包下新建StringUtil类,编写自定义的一些字符串工具类(如邮箱校验,验证码生成)

public class StringUtil {

    /**
     * 邮箱校验
     *
     * @param email 邮箱
     * @return true or false
     */
    public static boolean checkEmail(String email) {
        String check = "^([a-zA-Z]|[0-9])(\\w|\\-)+@[a-zA-Z0-9]+\\.([a-zA-Z]{2,4})$";
        Pattern regex = Pattern.compile(check);
        Matcher matcher = regex.matcher(email);
        return matcher.matches();
    }

    /**
     * 密码校验(长度 6-18,至少包含1个字母)
     * @param password
     * @return
     */
    public static boolean checkPassword(String password) {
        String check = "(?=.*[a-zA-Z])[a-zA-Z0-9]{6,18}";
        Pattern regex = Pattern.compile(check);
        Matcher matcher = regex.matcher(password);
        return matcher.matches();
    }

    /**
     * 随机生成六位数字验证码
     */
    public static String randomSixCode() {
        return String.valueOf(new Random().nextInt(899999) + 100000);
    }

    /**
     * 随机生成加密盐(4位随机字母 + 4位固定特殊字符)
     */
    public static String randomEncryptedSalt() {
        return RandomStringUtils.randomAlphabetic(4) + "#!$@";
    }
}

至此,基本完成了80%的工作,剩下的就是最重要的部分了,service业务层的设计,这部分我会详细介绍

🍊 邮箱业务

邮箱服务

service 包下创建 MailService 邮箱服务类,这里只列出了普通邮件和HTML邮件的代码,其他类型邮件网上也有的

javaMailSender Java内部封装好的邮箱发送类,只需要导入对应的依赖,直接注入即可

from 通过@Value注解读取 application-email 的username字段(也就是自己的邮箱)

message.setForm 中的三个参数,第一个是发件人(也就是自己),第二个是发件人昵称,也就是下面图片框出来的,第三个参数编码

在这里插入图片描述

@Component
public class MailService  {

    @Resource
    private JavaMailSender javaMailSender;

    @Value("${spring.mail.username}")
    private String from;

    /**
     * 发送简单的邮箱
     *
     * @param to 收件人
     * @param theme 标题
     * @param content 正文内容
     * @param cc 抄送
     */
    public void sendSimpleMail(String to, String theme, String content, String... cc) {
        // 创建邮件对象
        SimpleMailMessage message = new SimpleMailMessage();
        
        try {
            message.setFrom(String.valueOf(new InternetAddress(from, "小袁博客平台", "UTF-8")));      // 发件人
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        message.setTo(to);          // 收件人
        message.setSubject(theme);  // 标题
        message.setText(content);   // 内容
        
        if (ArrayUtils.isNotEmpty(cc)) {
            message.setCc(cc);
        }

        // 发送
        javaMailSender.send(message);
    }

    /**
     * 发送HTML邮件
     *
     * @param to      收件人地址
     * @param subject 邮件主题
     * @param content 邮件内容
     * @param cc      抄送地址
     * @throws MessagingException 邮件发送异常
     */
    public void sendHtmlMail(String to, String subject, String content, String... cc) throws MessagingException {
        MimeMessage message = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setFrom(from);
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(content, true);
        if (ArrayUtils.isNotEmpty(cc)) {
            helper.setCc(cc);
        }
        javaMailSender.send(message);
    }
}
线程池配置

先在主目录创建 config 包,包下新建 ThreadPoolConfig 类,这是线程池的配置类,帮助统一管理线程,线程池的其他作用就不一一介绍啦,挺多的,可以B站找视频学习

@Configuration
@EnableAsync // 开启线程池
public class ThreadPoolConfig {

    @Bean("taskExecutor")
    public Executor asyncServiceExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        // 设置核兴线程数
        executor.setCorePoolSize(5);
        // 设置最大线程数
        executor.setMaxPoolSize(20);
        // 设置队列大小
        executor.setQueueCapacity(Integer.MAX_VALUE);
        // 设置线程活跃时间 60s
        executor.setKeepAliveSeconds(60);
        // 设置默认线程名称
        executor.setThreadNamePrefix("小袁博客平台");
        // 是否所有任务执行完毕后关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 执行初始化
        executor.initialize();
        return executor;
    }
}
线程服务

service 包下新建 threadService 线程服务类

发送邮箱是一件很耗时的操作,如果不开辟线程去执行在主线程执行的话,会等待很久导致请求超时,而且我们发送验证码也不需要等到邮件到了才返回给前端提示

所以我们开辟线程去执行邮箱发送操作,不占用主线程运行

@Component
public class ThreadService {

    @Autowired
    private MailService mailService;

    /**
     * 发送邮箱
     * @param to 收件人
     * @param theme 主题
     * @param content 内容
     */
    @Async("taskExecutor")
    public void sendSimpleMail(String to, String theme, String content) {
        mailService.sendSimpleMail(to, theme, content);
    }
}
公共服务

新建 CommonService 公共服务接口类,注意是接口,这里讲解一下发送验证码的设计

在这里插入图片描述
使用权限码校验防止故意重复使用该接口,每次发送验证码请求前都先请求一个随机的权限码(有效时间越短越好,当然也不要太短,5 - 10s即可)

public interface CommonService {

    /**
     * 获取请求权限码
     * @param emailJson 邮箱
     * @return
     */
    R getRequestPermissionCode(String emailJson);

    /**
     * 发送邮箱验证码
     * @param loginParam (邮箱和权限码)
     * @return
     */
    R sendEmailCode(LoginParam loginParam);
}

新建 CommonServiceImpl 类实现 CommonService 接口

代码每行都写有注释,内容不难,参数校验 -> 邮箱校验 -> 权限码校验 -> 发送验证码

@Component
public class CommonServiceImpl implements CommonService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private ThreadService threadService;

    @Override
    public R getRequestPermissionCode(String emailJson) {
        // 非空校验
        if (StringUtils.isBlank(emailJson)) return R.error(HttpStatusEnum.PARAM_ILLEGAL);

        // JSON转换,提取email的值
        String email = JSON.parseObject(emailJson).getString("email").trim();
        // 邮箱校验
        if (!StringUtil.checkEmail(email)) {
            return R.error(HttpStatusEnum.EMAIL_ERROR);
        }

        // 随机生成权限码
        String permissionCode = UUID.randomUUID().toString();

        // 存入redis,缓存10s
        redisTemplate.opsForValue().set(RedisConstant.EMAIL_REQUEST_VERIFY + email, permissionCode, RedisConstant.EXPIRE_TEN_SECOND, TimeUnit.SECONDS);
        return R.ok().data("permissionCode", permissionCode);
    }

    @Override
    public R sendEmailCode(LoginParam loginParam) {
        if (loginParam == null) return R.error(HttpStatusEnum.PARAM_ILLEGAL);

        // 获取权限码和邮箱
        String email = loginParam.getEmail();
        String permissionCode = loginParam.getCode();
        // 参数校验
        if (StringUtils.isAnyBlank(email, permissionCode)) {
            return R.error(HttpStatusEnum.PARAM_ILLEGAL);
        }else if (!StringUtil.checkEmail(email)) {
            // 邮箱校验
            return R.error(HttpStatusEnum.EMAIL_ERROR);
        }else {
            // 权限码比对
            String rightCode = redisTemplate.opsForValue().get(RedisConstant.EMAIL_REQUEST_VERIFY + email);
            if (!permissionCode.equals(rightCode)) {
                // 不通过
                return R.error(HttpStatusEnum.ILLEGAL_OPERATION);
            }
        }

        // 全部通过

        // 随机生成6位数字验证码
        String code = StringUtil.randomSixCode();

        // 正文内容
        String content = "亲爱的用户:\n" +
                "您此次的验证码为:\n\n" +
                code + "\n\n" +
                "此验证码5分钟内有效,请立即进行下一步操作。 如非你本人操作,请忽略此邮件。\n" +
                "感谢您的使用!";

        // 发送验证码
        threadService.sendSimpleMail(email, "您此次的验证码为:" + code, content);
        // 丢入缓存,设置5分钟过期
        redisTemplate.opsForValue().set(RedisConstant.EMAIL + email, code, RedisConstant.EXPIRE_FIVE_MINUTE, TimeUnit.SECONDS);
        return R.ok();
    }
}
邮箱服务器接口

邮箱服务已经设计好了,接下来写邮箱服务的接口,然后前端运行测试一下,邮箱功能是否正常

🎯 接口路径如果不和我这个一样的话,前端api下的请求记得也要改路径

@RestController
@RequestMapping("/common")
@CrossOrigin
public class CommonController {

    @Autowired
    private CommonService commonService;

    // 权限码请求接口
    @PostMapping("code/request")
    public R getRequestPermissionCode(@RequestBody String emailJson) {
        return commonService.getRequestPermissionCode(emailJson);
    }

    // 邮箱验证码接口
    @PostMapping("code/email")
    public R sendEmailCode(@RequestBody LoginParam loginParam) {
        return commonService.sendEmailCode(loginParam);
    }
}

启动后端项目,启动前端项目,成功发送邮箱验证码,没有问题

在这里插入图片描述

在这里插入图片描述

🍋 登录注册业务

新建 UserService 接口类

public interface UserService extends IService<User> {

    /**
     * 登录
     * @param loginParam (邮箱和密码)
     * @return
     */
    R login(LoginParam loginParam);

    /**
     * 注册
     * @param loginParam (邮箱、密码、确认密码、验证码)
     * @return
     */
    R register(LoginParam loginParam);

    /**
     * 找回密码
     * @param loginParam (邮箱、密码、验证码)
     * @return
     */
    R findPassword(LoginParam loginParam);
}

新建 UserServiceImpl 类实现 UserService 接口

@Service
@Transactional
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Override
    public R login(LoginParam loginParam) {
        return null;
    }

    @Override
    public R register(LoginParam loginParam) {
    	return null;
    }

    @Override
    public R findPassword(LoginParam loginParam) {
        return null;
    }
}

登录注册找回密码我一个一个来介绍

参数校验 -> 用户是否存在 -> 密码是否正确 -> 登录成功

	@Override
    public R login(LoginParam loginParam) {
        if (loginParam == null) return R.error(HttpStatusEnum.PARAM_ILLEGAL);

        // 获取参数
        String email = loginParam.getEmail();
        String password = loginParam.getPassword();

        if (StringUtils.isAnyBlank(email, password)) {
            // 非空
            return R.error(HttpStatusEnum.PARAM_ILLEGAL);
        }else if (!StringUtil.checkEmail(email)) {
            // 邮箱格式校验
            return R.error(HttpStatusEnum.EMAIL_ERROR);
        }else if (!StringUtil.checkPassword(password)) {
            // 密码格式
            return R.error(HttpStatusEnum.PASSWORD_ERROR);
        }

        // 构件条件对象 select salt from user where email = #{email} limit 1
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.select("salt");
        wrapper.eq("email", email);
        wrapper.last("limit 1");

        // 查询结果
        User user = this.baseMapper.selectOne(wrapper);
        if (user == null) {
            // 用户不存在
            return R.error(HttpStatusEnum.USER_NOT_EXIST);
        }

        // 获取加密盐
        String salt = user.getSalt();
        // 重新设置条件 select id from user where email = #{email} and password #{password} limit 1
        wrapper.clear();
        wrapper.select("id");
        wrapper.eq("email", email);
        wrapper.eq("password", DigestUtils.md5Hex(password + salt));
        wrapper.last("limit 1");
        // 查询用户
        user = this.baseMapper.selectOne(wrapper);

        return user == null ? R.error(HttpStatusEnum.PASSWORD_ERROR) : R.ok();
    }

参数校验 -> 邮箱是否被注册 -> 验证码比对-> 删除redis验证码 -> 生成加密盐 -> 加密密码 -> 注册用户

	@Override
    public R register(LoginParam loginParam) {
        if (loginParam == null) return R.error(HttpStatusEnum.PARAM_ILLEGAL);

        // 获取参数
        String email = loginParam.getEmail();
        String password = loginParam.getPassword();
        String passwordConfirm = loginParam.getPasswordConfirm();
        String code = loginParam.getCode();

        if (StringUtils.isAnyBlank(email, password, passwordConfirm, code)) {
            // 非空
            return R.error(HttpStatusEnum.PARAM_ILLEGAL);
        }else if (!StringUtil.checkEmail(email)) {
            // 邮箱格式校验
            return R.error(HttpStatusEnum.EMAIL_ERROR);
        }else if (!password.equals(passwordConfirm)) {
            // 密码一致校验
            return R.error(HttpStatusEnum.PASSWORD_INCONSISTENT);
        }else if (!StringUtil.checkPassword(password) || code.length() != 6) {
            // 密码格式和验证码长度校验
            return R.error(HttpStatusEnum.PARAM_ILLEGAL);
        }

        // 构造查询条件对象
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.select("id");
        wrapper.eq("email", email);
        wrapper.last("limit 1");

        // 查询用户,是否存在
        if (this.baseMapper.selectOne(wrapper) != null) {
            return R.error(HttpStatusEnum.EMAIL_ALREADY_EXIST);
        }

        // 获取正确的验证码
        String rightCode = redisTemplate.opsForValue().get(RedisConstant.EMAIL + email);
        if (!code.equals(rightCode)) {
            // 验证码比对
            return R.error(HttpStatusEnum.CODE_ERROR);
        }

        // 删除验证码
        redisTemplate.delete(RedisConstant.EMAIL + email);

        // 注册用户
        User user = new User();
        // 获取加密盐
        String salt = StringUtil.randomEncryptedSalt();
        // 邮箱
        user.setEmail(email);
        // 密码加密(原明文密码 + 随机加密盐) md5加密
        user.setPassword(DigestUtils.md5Hex(password + salt));
        // 加密盐
        user.setSalt(salt);

        // 插入数据
        return this.baseMapper.insert(user) == 0 ? R.error(HttpStatusEnum.UNKNOWN_ERROR) : R.ok();
    }

参数校验 -> 用户是否存在 -> 验证码校验 -> 删除redis验证码 -> 覆盖密码

	@Override
    public R findPassword(LoginParam loginParam) {
        if (loginParam == null) return R.error(HttpStatusEnum.PARAM_ILLEGAL);

        // 获取参数
        String email = loginParam.getEmail();
        String password = loginParam.getPassword();
        String code = loginParam.getCode();

        if (StringUtils.isAnyBlank(email, password, code)) {
            // 非空
            return R.error(HttpStatusEnum.PARAM_ILLEGAL);
        }else if (!StringUtil.checkEmail(email)) {
            // 邮箱格式校验
            return R.error(HttpStatusEnum.EMAIL_ERROR);
        }else if (!StringUtil.checkPassword(password) || code.length() != 6) {
            // 密码格式和验证码长度校验
            return R.error(HttpStatusEnum.PARAM_ILLEGAL);
        }

        // 构造查询条件对象
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.select("id", "salt");
        wrapper.eq("email", email);
        wrapper.last("limit 1");

        // 查询用户,是否存在
        User user = this.baseMapper.selectOne(wrapper);
        if (user == null) {
            return R.error(HttpStatusEnum.USER_NOT_EXIST);
        }

        // 获取正确的验证码
        String rightCode = redisTemplate.opsForValue().get(RedisConstant.EMAIL + email);
        if (!code.equals(rightCode)) {
            // 验证码比对
            return R.error(HttpStatusEnum.CODE_ERROR);
        }

        // 删除验证码
        redisTemplate.delete(RedisConstant.EMAIL + email);
        
        // 修改密码
        User user1 = new User();
        user1.setId(user.getId());
        user1.setPassword(DigestUtils.md5Hex(password + user.getSalt()));
        
        // 修改
        return this.baseMapper.updateById(user1) == 0 ? R.error(HttpStatusEnum.UNKNOWN_ERROR) : R.ok();
    }
接口

🎯 接口路径如果不和我这个一样的话,前端api下的请求记得也要改路径

@RestController
@RequestMapping("/user")
@CrossOrigin
public class UserController {

    @Autowired
    private UserService userService;

    // 登录
    @PostMapping("login")
    public R login(@RequestBody LoginParam loginParam) {
        return userService.login(loginParam);
    }

    // 注册
    @PostMapping("register")
    public R register(@RequestBody LoginParam loginParam) {
        return userService.register(loginParam);
    }

    // 找回密码
    @PostMapping("findPassword")
    public R findPassword(@RequestBody LoginParam loginParam) {
        return userService.findPassword(loginParam);
    }
}

🥭 视频演示

最后看看视频演示的效果

SpringBoot+Vue实现邮箱登录注册找回密码

🌹 结束语

好了,整篇的教程呢到这也就结束,整篇教程即为原创一字一字手敲,也花了心思想怎么写怎么设计才能更好的直观简洁展示给大家,让大家能看懂

最后,关于教程还有什么不懂的可以评论区留言,我一定会回复的,或者有什么更好的建议和想法也可以在评论区留言,看到好的我会一一采纳,感谢大家的支持

再一次附上Gitee开源地址:https://gitee.com/yuandewei/Yuan-SpringBoot/tree/master 不用大伙翻上去复制了


🚀 注重版权,转载请注明原作者和原文链接

🌐 Open开袁 的官方网站已全面升级!探索更多精彩内容,尽在:https://open-yuan.com

在这里,你将发现丰富的编程资源、深度的技术文章,以及开源项目的地址,一起加入我们的技术交流社区吧!

📱 想要随时随地获取最新动态?微信搜索公众号“全栈小袁”,一手掌握最新项目动态和技术分享。让我们一起,开启技术的无限可能!🌈


Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐