通过邮箱实现注册,用户请求验证码完成注册操作。
导入依赖:
org.springframework.boot
spring-boot-starter-mail
org.springframework.boot
spring-boot-starter-amqp
将验证码丢到消息队列中,再由监听器消费请求
配置完后进行测试:
//JavaMailSender是专门用于发送邮件的对象,自动配置类已经提供了Bean
@Autowired
JavaMailSender sender;
@Test
void contextLoads() {
//SimpleMailMessage是一个比较简易的邮件封装,支持设置一些比较简单内容
SimpleMailMessage message = new SimpleMailMessage();
//设置邮件标题
message.setSubject("【南京信息工程大学教务处】关于近期学校对您的处分决定");
//设置邮件内容
message.setText("赵国成同学您好,经监控和教务巡查发现,您近期存在旷课、迟到、早退、上课刷抖音行为," +
"现已通知相关辅导员,请手写5000字书面检讨,并于2023年10月7号前交到辅导员办公室。");
//设置邮件发送给谁,可以多个,这里就发给你的QQ邮箱
message.setTo("[email protected]");
//邮件发送者,这里要与配置文件中的保持一致
message.setFrom("[email protected]");
//OK,万事俱备只欠发送
sender.send(message);
}
在AccountService中增加方法:
public interface AccountService extends IService , UserDetailsService {
Account findAccountByNameOrEmail(String text);
//type区分用户是注册还是更改密码,从而显示不同的文本提示;通过用户ip地址限制请求的频率
String RegisterEmailVerifyCode(String type, String email,String ip);
}
在服务代理中实现:
@Override
public String RegisterEmailVerifyCode(String type, String email, String ip) {
Random random=new Random();
//确保code为六位数
int code=random.nextInt(899999)+100000;
Map data=Map.of("type",type,"email",email,"code",code);
return type;
}
配置消息队列专门处理邮箱,新建RabbitConfig类:
@Configuration
public class RabbitConfig {
@Bean("emailQueue")
public Queue emailQueue(){
return (Queue) QueueBuilder.durable("mail").build();
}
}
编写FlowUtils进行过滤:
@Component
public class FlowUtils {
@Resource
StringRedisTemplate template;
public boolean limitOnceCheck(String key,int blockTime){
//正在冷却的状态
if (Boolean.TRUE.equals(template.hasKey(key))){
return false;
}
else {
//如果不在冷却时间内,可以发送邮件,发挥true,并更新冷却时间
template.opsForValue().set(key,"",blockTime, TimeUnit.SECONDS);
return true;
}
}
}
根据用户的ip进行过滤:
private boolean verifyLimit(String ip){
String key= Const.VERIFY_EMAIL_LIMIT+ip;
return flowUtils.limitOnceCheck(key,60);
}
RegisterEmailVerifyCode进行完善:
@Override
public String RegisterEmailVerifyCode(String type, String email, String ip) {
if (this.verifyLimit(ip)) {
Random random = new Random();
//确保code为六位数
int code = random.nextInt(899999) + 100000;
Map data = Map.of("type", type, "email", email, "code", code);
amqpTemplate.convertAndSend("mail", data);
template.opsForValue()
.set(Const.VERIFY_EMAIL_DATA + email, String.valueOf(code), 3, TimeUnit.MINUTES);
return null;
}else {
return "您的请求过于频繁,请稍后再试";
}
}
同一时间可能会被多次调用,此方法为线程不安全,因此需要上锁synchronized(ip.intern())
创建listener包,新建MailQueueListener类:
@Component
@RabbitListener(queues = "mail")
public class MailQueueListener {
@Resource
JavaMailSender sender;
@Value("${spring.mail.username}")
String username;
@RabbitHandler
public void sendMailMessage(Map data){
String email=(String) data.get("email");
Integer code=(Integer) data.get("code");
String type =(String) data.get("type");
SimpleMailMessage message=switch (type){
case "register"->
createMessage("欢迎注册","验证码为:"+code+"有效时间为3分钟",email);
case "reset"->
createMessage("你的密码重置邮件","验证码为:"+code+"有效时间为3分钟",email);
default -> null;
};
if (message==null)return;
sender.send(message);
}
private SimpleMailMessage createMessage(String title,String content,String email){
SimpleMailMessage message=new SimpleMailMessage();
message.setSubject(title);
message.setText(content);
message.setTo(email);
message.setFrom(username);
return message;
}
}
编写测试接口:
@RestController
@RequestMapping("/api/auth")
public class AuthorizeController {
@Resource
AccountService service;
@GetMapping("/ask-code")
public RestBean askVerifyCode(@RequestParam String email,
@RequestParam String type,
HttpServletRequest request) {
String message = service.RegisterEmailVerifyCode(type, email, request.getRemoteAddr());
return message== null ? RestBean.failure(400, message) : RestBean.success();
}
}
注意确认在security配置中将测试地址放行。
当请求验证码时,首先进入对应的处理方法,调用service中的RegisterEmailVertifyCode方法,将目标邮箱和类型传入,通过httpServlet得到请求验证码的主机信息,在邮箱验证码方法中,通过random随机生成六位验证码,存入map中,通过amqpTemplate.convertAndSend(“mail”, data)将数据传入消息队列中,此处我们在rabbitconfig中建立了名为mail的emailqueue,接着利用redis数据库进行计时,将对应邮箱的验证码设置为3分钟过期,每次请求的ip地址设置冷却时间60秒,在每次请求前对ip地址进行过滤,最后设置rabbit监听器监听消息队列,一旦消息队列中有邮件数据,则进行读取并利用javaemail进行发送。