目录
- 开通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就是你要发送到那个邮箱
-这是成功的效果
在网上找了几个别人做的前台效果图,大家可以参考一下