【Java】Hutool发送邮件功能

目录

  • 开通qq邮箱的stmp
  • 实战
    • pom.xml
    • application.yml
    • controller
    • service
    • 实体类
    • 辅助类

需要实现一个通过邮箱找回密码的功能

  • 正常来说,找回密码的验证码,一般来说,都是通过手机号来找回的居多,那为什么会有通过邮箱找回的方式
  • 该说不说,免费的舒服

在这里插入图片描述

  • 业务的大致流程

开通qq邮箱的stmp

在这里插入图片描述

  • 点击设置
    在这里插入图片描述
  • 点击账号
    在这里插入图片描述
  • 正常这里是开启服务,因为我已经开启过,所以显示管理服务
  • 会需要手机号发生一段话给某个指定的手机号来做验证,存在发不通的情况(建议换一个号码,指定的手机号,有两个,都试一下)
  • 到这一步,会生成一个密码。你可以理解为邮箱的密码

实战

pom.xml

  <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
            <version>5.6.5</version>
        </dependency>

        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.1</version>
        </dependency>

         <!--邮件-->
        <dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
            <version>1.6.2</version>
        </dependency>
  • javax.mail对应的依赖需要额外导入

application.yml

#邮件配置
email:
  #邮箱账户
  emailAccount:  123@qq.com
  #邮箱密码
  emailPassword: 123
  #发件人名称
  senderName: 发送人名称
  ##发件服务器 stmp的服务协议的服务器
  sendServer: smtp.qq.com
  #发件服务器是否遵循安全协议(0,不是 1是,默认0)
  sendServerIsSSL: 1
  ##发件服务器端口(一般是若sendServerIsSSL = 1,端口则是25,若不是,则为465)
  sendPort: 465
  • 只放了关键代码,修改邮箱账号以及邮箱密码(上面开通qq邮箱时,返回的密码)改为自己的
  • 注意: 这是qq邮箱的设置版本,如果是163邮箱版本,个别配置需要微调

controller

package com.zyee.iopace.web.controller;

@Api(tags = "用户管理")
@RestController
@RequestMapping("/user")
public class UserController {
  
    @Autowired
    private UserService userService;

    @ApiOperation(value = "通过邮箱发送验证码")
    @GetMapping("/sendEmailCode")
    public ResponseResult sendEmailCode(@RequestParam String email){
        return userService.sendEmailCode(email);
    }

    @ApiOperation(value = "验证邮箱的验证码以及重置密码")
    @PostMapping("/checkEmailCode")
    public ResponseResult checkEmailCode(@RequestBody CheckEmailVo checkEmailVo){
        return userService.checkEmailCode(checkEmailVo);
    }

}

service

    /**
     * 发送邮件code
     */
    public static  final String EMAIL_CODE = "email:code:";
    /**
     * 发送邮件次数
     */
    public static  final String EMAIL_COUNT = "email:count:";
    /**
     * 验证邮箱验证码错误次数
     */
    public static  final String EMAIL_CHECK_COUNT = "email:check:count:";

  @Value("${email.emailAccount}")
    private String emailAccount;

    @Value("${email.emailPassword}")
    private String emailPassword;

    @Value("${email.senderName}")
    private String senderName;

    @Value("${email.sendServer}")
    private String sendServer;

    @Value("${email.sendServerIsSSL}")
    private Integer sendServerIsSSL;

    @Value("${email.sendPort}")
    private Integer sendPort;

 @Override
    public ResponseResult sendEmailCode(String email) {
        if(StringUtils.isEmpty(email)){
            return ResponseResult.FAILURE("邮箱不能为空!");
        }
        User user = getOne(new LambdaQueryWrapper<User>().eq(User::getEmail, email));
        if(Objects.isNull(user)){
            return ResponseResult.FAILURE("请输入正确的邮箱!");
        }

        String key = RedisConstant.EMAIL_COUNT+user.getId();
        if(checkSendLimit(key,5)){
            return ResponseResult.FAILURE("一个小时以内只能发送5次!");
        }

        //产生6位数的随机码
        Random random = new Random();
        int code = random.nextInt(900000) + 10000;

        //发送验证码
        Boolean flag = sendEmailUtil.sendEmailMsg(emailAccount, emailPassword, senderName, sendServer, sendServerIsSSL, sendPort, email,code);
        if(flag){
            String codeKey = RedisConstant.EMAIL_CODE+user.getId();
            //验证码的有效时长为5分钟
            redisUtils.set(codeKey,code,5 * 60);
            // 增加发送次数
            incrementSendCount(key,3600L);
        }else{
            return ResponseResult.FAILURE("发送邮箱验证码失败,请检查邮箱是否合理!");
        }
        return ResponseResult.SUCCESS();
    }

    @Override
    public ResponseResult checkEmailCode(CheckEmailVo checkEmailVo) {
        if(Objects.isNull(checkEmailVo.getCode()) ||
                StringUtils.isEmpty(checkEmailVo.getEmail()) ||
                StringUtils.isEmpty(checkEmailVo.getPwd())){
           return ResponseResult.FAILURE("参数不能为空!");
        }

        //先验证验证码是否正确
        User user = getOne(new LambdaQueryWrapper<User>().eq(User::getEmail, checkEmailVo.getEmail()));
        if(Objects.isNull(user)){
            return ResponseResult.FAILURE("请输入正确的邮箱!");
        }

        //错误次数key
        String checkKey = RedisConstant.EMAIL_CHECK_COUNT+user.getId();
        if(checkSendLimit(checkKey,3)){
            return ResponseResult.FAILURE("5分钟之内错误3次!");
        }

        String codekey = RedisConstant.EMAIL_CODE+user.getId();
        Integer code = redisUtils.getInteger(codekey);
        if(Objects.isNull(code)){
            return ResponseResult.FAILURE("验证码已失效!");
        }else{
            if(code.intValue() != checkEmailVo.getCode().intValue()){
                //错误增加次数
                incrementSendCount(checkKey,5 * 60L);
                return ResponseResult.FAILURE("验证码错误,请重新输入!");
            }

            //验证码正常修改密码
            User updateUser = new User();
            updateUser.setId(user.getId());
            updateUser.setPassword(checkEmailVo.getPwd());
            updateById(updateUser);
        }
        return ResponseResult.SUCCESS();
    }

    /**
     * 验证次数
     * @param key
     * @return
     */
    public boolean checkSendLimit(String key,Integer maxSendCount) {
        Integer sendCount = 0;
        // 最大发送次数限制
        // 获取当前发送次数
        sendCount = redisUtils.getInteger(key);
        if(Objects.isNull(sendCount)){
            sendCount = 0;
        }
        // 超过发送次数限制返回 true,否则返回 false
        return sendCount >= maxSendCount;
    }

    /**
     * 增加验证码发送次数
     * @param key
     * @param expire 过期秒数
     */
    public void incrementSendCount(String key,Long expire) {
        // 获取当前发送次数
        Object countObj = redisUtils.get(key);
        int sendCount = (countObj != null) ? (Integer) countObj : 0;
        // 增加发送次数
        sendCount++;


        if(sendCount != 1){
            expire = redisUtils.getExpire(key);
        }

        // 存储一小时内的发送次数
        redisUtils.set(key, sendCount, expire);
    }
  • 三个变量是redis的变量
  • 主要是用来控制,发送邮箱验证码时,一个用户1个小时以内只能发送5次
  • 输入验证码进行修改密码时,5分钟只能只能错3次,防止有人暴力破解

实体类

package com.zyee.iopace.web.vo.email;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class EmailInfoVo {

    /**
     * 邮箱账户
     */
    private String emailAccount;

    /**
     * 邮箱密码
     */
    private String emailPassword;

    /**
     * 发件人名称
     */
    private String senderName;


    /**
     * 发件服务器
     */
    private String sendServer;

    /**
     * 发件服务器是否遵循安全协议(0,不是 1是,默认0)
     */
    private Integer sendServerIsSSL = 0;

    /**
     * 发件服务器端口(一般是若sendServerIsSSL = 1,端口则是25,若不是,则为465)
     */
    private Integer sendPort;

}

辅助类

package com.zyee.iopace.web.utils;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.mail.MailAccount;
import cn.hutool.extra.mail.MailUtil;
import com.zyee.iopace.web.vo.email.EmailInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class SendEmailUtil {

    public static void main(String[] args) {
        String emailAccount = "123@qq.com";
        String emailPassword = "123";
        String senderName = "465";
        String sendServer = "smtp.qq.com";
        Integer sendServerIsSSL = 1;
        Integer sendPort = 465;
        String toAccount = "123@qq.com";
        //产生6位数的随机码
        Random random = new Random();
        int code = random.nextInt(900000) + 10000;
        SendEmailUtil sendEmailUtil = new SendEmailUtil();
        sendEmailUtil.sendEmailMsg(emailAccount, emailPassword, senderName, sendServer,sendServerIsSSL ,sendPort,toAccount,code);
    }

    /**
     * 发送邮箱消息
     * @param emailAccount
     * @param emailPassword
     * @param senderName
     * @param sendServer
     * @param sendServerIsSSL
     * @param sendPort
     * @param code
     */
    public  Boolean sendEmailMsg(String emailAccount,
                                     String emailPassword,
                                     String senderName,
                                     String sendServer,
                                     Integer sendServerIsSSL,
                                     Integer sendPort,
                                     String toAcccount,
                                 Integer code) {
        Boolean flag = false;
        try {
            EmailInfoVo info = new EmailInfoVo();
            info.setEmailAccount(emailAccount);
            info.setEmailPassword(emailPassword);
            info.setSendPort(sendPort);
            //遵循stmp的服务协议的服务器,可以百度,很多的
            info.setSendServer(sendServer);
            info.setSenderName(senderName);
            info.setSendServerIsSSL(sendServerIsSSL);
            MailAccount account = getMailAccount(info);
            String send = MailUtil.send(account, toAcccount, "【大气气候与环境观测网站】您的注册码", "您的验证码是:" + code + ",五分钟有效,请及时完成注册。若不是本人操作请忽略", false);
            log.info("发送验证码 msg={} 验证码={}",toAcccount,code);
            flag = true;
        }catch (Exception e){
            log.info("发送验证码 出现异常msg={}",e.getMessage());
        }
        return flag;
    }


    /**
     * 获取邮箱参数对象
     *
     * @param emailInfo
     * @return
     */
    private static MailAccount getMailAccount(EmailInfoVo emailInfo) {
        MailAccount account = new MailAccount();

        if (ObjectUtil.isNotEmpty(emailInfo.getSenderName())) {
            StringBuilder sb = new StringBuilder();
            sb.append(emailInfo.getSenderName())
                    .append('<')
                    .append(emailInfo.getEmailAccount())
                    .append('>');
            account.setFrom(sb.toString());
        } else {
            account.setFrom(emailInfo.getEmailAccount());
            account.setUser(emailInfo.getEmailAccount());
        }
        account.setPass(emailInfo.getEmailPassword());
        account.setHost(emailInfo.getSendServer());
        account.setPort(emailInfo.getSendPort());
        account.setAuth(true);
        account.setSocketFactoryClass("javax.net.ssl.SSLSocketFactory");
        account.setSocketFactoryFallback(true);
        account.setSocketFactoryPort(emailInfo.getSendPort());
        if (emailInfo.getSendServerIsSSL() == 0) {
            account.setSslEnable(false);
        } else {
            account.setSslEnable(true);
        }
        account.setTimeout(3000);
        return account;
    }
}

  • 直接运行main方法,调整成你的邮箱和密码
  • toAccount就是你要发送到那个邮箱
    在这里插入图片描述
    -这是成功的效果

在网上找了几个别人做的前台效果图,大家可以参考一下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

Luminar Neo - AI智能修图软件超越PS和LR,简单易用又高效!

很多人都想美化自己的风景和人物的图片&#xff0c;得到更加美丽耀眼的效果。然而&#xff0c;专业摄影师和设计师在电脑上使用的后期工具如 Photoshop 和 LightRoom 过于复杂。 通常为了一些简单的效果&#xff0c;你必须学习许多教程。而一些针对小白用户的“一键式美颜/美化…

Python第二语言(八、Python包)

目录 1. 什么是Python包 2. 创包步骤 2.1 new包 2.2 查看创建的包 2.3 拖动文件到包下 3. 导入包 4. 安装第三方包 4.1 什么是第三方包 4.2 安装第三方包-pip 4.3 pip网络优化 1. 什么是Python包 包下有__init__.py就是包&#xff0c;无__init__.py就是文件夹。于Ja…

开发没有尽头,尽力既是完美

最近遇到了一些难题&#xff0c;开发系统总有一些地方没有考虑周全&#xff0c;偏偏用户使用的时候“完美复现”了这个隐藏的Bug...... 讲道理创业一年之久为了生存&#xff0c;我一直都有在做复盘&#xff0c;复盘的核心就是&#xff1a;如何提升营收、把控开发质量&#xff0…

gdb调试器

目录 一、前言 debug和release 二、调试操作 2.1、退出 quit 2.2、调试 run 2.3、打断点 b 2.4、查看断点 info b 2.5、查看代码 l 2.6、删除断点 d 2.7、逐过程 n 2.8、打印变量内容 p 2.9、逐语句&#xff08;进入函数&#xff09; s 2.10、查看函数调用堆栈 …

先进封装技术的一些优缺点探讨

半导体封装技术是半导体制造过程中的关键环节&#xff0c;它不仅保护了芯片免受物理损伤&#xff0c;还提供了电气连接和散热功能。随着技术的发展&#xff0c;出现了多种先进的封装技术&#xff0c;每种技术都有其特定的应用场景和优缺点。 --> 1. 传统封装技术 【优点】&…

微服务之基本介绍

一、微服务架构介绍 1、微服务架构演变过程 单体应用架构->垂直应用架构一>分布式架构一>SOA架构-->微服务架构 ① 单体应用架构 互联网早期&#xff0c; 一般的网站应用流量较小&#xff0c;只需一个应用&#xff0c;将所有功能代码都部署在一起就可以&#x…

嵌入式仪器模块:波形发生器模块(嵌入式)

• 16 位分辨率 • 125 MHz 刷新率 • 支持生成 FSK/ASK 信号 应用场景 • 生成任意标准波形或用户自定义波形 • 在特殊协议通信中模拟某个波形 • 无线充电&#xff08;信号调制&#xff09; 道114输出阻抗Low-ZLow-ZLow-Z输出范围 5 V 5 V 6 V耦合DCDCDC带宽4 MHz10 M…

【小沐学Python】Python实现Web服务器(CentOS下打包Flask)

文章目录 1、简介2、下载Python3、编译Python4、安装PyInstaller5、打包PyInstaller6、相关问题6.1 ImportError: urllib3 v2 only supports OpenSSL 1.1.1, currently the ssl module is compiled with OpenSSL 1.0.2k-fips 26 Jan 2017. See: https://github.com/urllib3/url…

AXI Quad SPI IP核中的STARTUPEn原语参数

启动STARTUPEn Primitive &#xff08;原语&#xff09;参数在 FPGA的主 SPI模式下非常有用。当你启用这个参数时&#xff0c;对于 7 系列设备&#xff0c;STARTUPE2 原语会被包含在设计中&#xff1b;而对于 UltraScale™ 设备&#xff0c;则是 STARTUPE3 原语。这些原语在 FP…

基于NodeJs 的Vue安装和创建项目

基于NodeJs 的Vue安装和创建项目 一、Node.js的下载与安装 下载地址&#xff1a; https://nodejs.org/en/download/prebuilt-installer 安装完之后&#xff0c;启动 cmd命令行&#xff0c;验证 Node.js 是否安装成功 二、配置npm的全局模块的存放路径以及缓存的路径 注&…

大模型应用工程化过程

近年来&#xff0c;以人工智能为代表的新一代信息技术加速应用&#xff0c;特 别是基于大模型、大数据、大算力的 ChatGPT 的发布&#xff0c;标志着人 工智能技术取得里程碑式突破&#xff0c;推动科技创新进入新阶段。随着 大模型技术的迅猛发展和场景价值的不断涌现&#xf…

HTML-CSS练习例子

HTML CSS 练习 https://icodethis.com 作为前端练习生。不敲代码只看&#xff0c;入门是很慢的&#xff0c;所以直接实战是学习前端最快的途径之一。 这个网站练习HTML CSS的&#xff0c;可以打开了解一下&#xff0c;可以每天打卡&#xff0c;例子简单&#xff0c;循序渐进&…

每日5题Day19 - LeetCode 91 - 95

每一步向前都是向自己的梦想更近一步&#xff0c;坚持不懈&#xff0c;勇往直前&#xff01; 第一题&#xff1a;91. 解码方法 - 力扣&#xff08;LeetCode&#xff09; class Solution {public int numDecodings(String s) {int n s.length();//注意我们dp的范围是n1int[] d…

AtCoder Beginner Contest 356 G. Freestyle(凸包+二分)

题目 思路来源 quality代码 题解 对n个泳姿点(ai,bi)建凸包&#xff0c;实际上是一个上凸壳&#xff0c; 对于询问(ci,di)来说&#xff0c;抽象画一下这个图&#xff0c;箭头方向表示询问向量 按x轴排增序&#xff0c;并且使得后面的y不小于前面的y&#xff0c;因为总可以多…

ThreadCache线程缓存

一.ThreadCache整体结构 1.基本结构 定长内存池利用一个自由链表管理释放回来的固定大小的内存obj。 ThreadCache需要支持申请和释放不同大小的内存块&#xff0c;因此需要多个自由链表来管理释放回来的内存块.即ThreadCache实际上一个哈希桶结构&#xff0c;每个桶中存放的都…

LLM的基础模型8:深入注意力机制

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

SpringBoot之静态资源

默认静态资源路径 classpath:/META-INF/resources/classpath:/resources/classpath:/static/classpath:/public/ 静态资源路径下的文件&#xff0c;可以通过根目录访问 resources 文件夹的文件如下图所示&#xff1a; 启动项目&#xff0c;分别访问以下路径&#xff1a; ht…

STM32 proteus + STM32Cubemx仿真教程(第一课LED教程)

文章目录 前言一、STM32点亮LED灯的原理1.1GPIO是什么1.2点亮LED灯的原理 二、STM32Cubemx创建工程三、proteus仿真电路图四、程序代码编写1.LED灯操作函数介绍HAL_GPIO_WritePin函数原型参数说明示例代码 HAL_GPIO_TogglePin函数原型参数说明示例代码 2.代码编写3.烧写程序 总…

微服务开发与实战Day04 - 网关路由和配置

一、网关路由 网关&#xff1a;就是网络的关口&#xff0c;负责请求的路由、转发、身份校验。 在SpringCloud中网关的实现包括两种&#xff1a; 1. 快速入门 Spring Cloud Gateway 步骤&#xff1a; ①新建hm-gateway模块 ②引入依赖pom.xml(hm-gateway) <?xml version…