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


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

    导入依赖:

    
    			org.springframework.boot
    			spring-boot-starter-mail
    		
    		
    			org.springframework.boot
    			spring-boot-starter-amqp
    		
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    将验证码丢到消息队列中,再由监听器消费请求

    配置完后进行测试:

    //JavaMailSender是专门用于发送邮件的对象,自动配置类已经提供了Bean
    	@Autowired
    	JavaMailSender sender;
    
    	@Test
    	void contextLoads() {
    		//SimpleMailMessage是一个比较简易的邮件封装,支持设置一些比较简单内容
    		SimpleMailMessage message = new SimpleMailMessage();
    		//设置邮件标题
    		message.setSubject("【南京信息工程大学教务处】关于近期学校对您的处分决定");
    		//设置邮件内容
    		message.setText("赵国成同学您好,经监控和教务巡查发现,您近期存在旷课、迟到、早退、上课刷抖音行为," +
    				"现已通知相关辅导员,请手写5000字书面检讨,并于2023年10月7号前交到辅导员办公室。");
    		//设置邮件发送给谁,可以多个,这里就发给你的QQ邮箱
    		message.setTo("786759086@qq.com");
    		//邮件发送者,这里要与配置文件中的保持一致
    		message.setFrom("18061946436@163.com");
    		//OK,万事俱备只欠发送
    		sender.send(message);
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在AccountService中增加方法:

    public interface AccountService extends IService , UserDetailsService {
        Account findAccountByNameOrEmail(String text);
        //type区分用户是注册还是更改密码,从而显示不同的文本提示;通过用户ip地址限制请求的频率
        String RegisterEmailVerifyCode(String type, String email,String ip);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在服务代理中实现:

       @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;
        }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    配置消息队列专门处理邮箱,新建RabbitConfig类:

    @Configuration
    public class RabbitConfig {
     @Bean("emailQueue")
        public Queue emailQueue(){
            return (Queue) QueueBuilder.durable("mail").build();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    编写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;
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    根据用户的ip进行过滤:

    private boolean verifyLimit(String ip){
          String key= Const.VERIFY_EMAIL_LIMIT+ip;
          return flowUtils.limitOnceCheck(key,60);
    }
    
    • 1
    • 2
    • 3
    • 4

    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 "您的请求过于频繁,请稍后再试";
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    同一时间可能会被多次调用,此方法为线程不安全,因此需要上锁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;
    
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    编写测试接口:

    @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();
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    注意确认在security配置中将测试地址放行。

    当请求验证码时,首先进入对应的处理方法,调用service中的RegisterEmailVertifyCode方法,将目标邮箱和类型传入,通过httpServlet得到请求验证码的主机信息,在邮箱验证码方法中,通过random随机生成六位验证码,存入map中,通过amqpTemplate.convertAndSend(“mail”, data)将数据传入消息队列中,此处我们在rabbitconfig中建立了名为mail的emailqueue,接着利用redis数据库进行计时,将对应邮箱的验证码设置为3分钟过期,每次请求的ip地址设置冷却时间60秒,在每次请求前对ip地址进行过滤,最后设置rabbit监听器监听消息队列,一旦消息队列中有邮件数据,则进行读取并利用javaemail进行发送。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 相关阅读:
    20240311 大模型快讯
    线性回归实战---Abalone鲍鱼年龄预测
    [时间序列预测]基于BP、RNN、LSTM、CNN-LSTM算法多特征(多影响因素)用电负荷预测[保姆级手把手教学]
    Vue基础5
    SpringMVC系列-4 参数解析器
    SpringBoot集成PageHelper分页实现
    如何理解低位交叉编址的公式T+(m-1)r
    第3章 信息收集
    浅谈放大器交调失真对系统的影响
    【Godot】【BUG】4.x NavigationAgent 导航不生效
  • 原文地址:https://blog.csdn.net/qq_40369277/article/details/133613019