邮件注册(一)验证码发送

通过邮箱实现注册,用户请求验证码完成注册操作。

导入依赖:


			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进行发送。

邮件注册(一)验证码发送_第1张图片

你可能感兴趣的:(java)