Java实现邮箱发送验证码并用Redis存储验证码

目录

场景:

1.完成发送邮箱的功能 

 ①引入email的依赖和Redis的相关依赖

②根据前端的请求数据和Redis存储结构考虑,创建对应的对象传输类,关于什么是Dto类可以参考我上一篇文章:

③创建发送验证码的工具类 EmailCodeUtils

 2.完成Redis的配置

3.完成Redis存储和读取数据 

4.登陆验证和发放jwt令牌 

         总结:


场景:

              老用户想通过邮箱获取验证码实现一键登录,或者新用户注册并登陆,前端发送获取验证码的请求-->后端生成验证码发送给目标邮箱,并存储于Redis缓存中->用户收到验证码-->填写验证码向后端发送登陆请求-->后端接收发送来的邮箱和验证码的JSON与Redis中获取的对比,响应给前端登陆成功与否   

1.完成发送邮箱的功能 

 ①引入email的依赖和Redis的相关依赖

②根据前端的请求数据和Redis存储结构考虑,创建对应的对象传输类,关于什么是Dto类可以参考我上一篇文章:

Dto类的使用以及Spring中对于前端请求处理的注意事项https://blog.csdn.net/qq_52692506/article/details/132025236

一、前端请求:

 1.验证码请求和请求体

2. 登陆验证请求及其请求体数据

            我们发现都带有他们都带有mail这个数据,同时登陆请求还需要验证码Code,而数据库对应的实体类中的成员变量不足以满足数据的接收,所以要创建Dto类完成接收

二、创建对应的Dto类:UserDto类:

                这里其实可以选择继承User类,这样的话只需要多加一个code成员就行,能接收到前端发来的数据就行

③创建发送验证码的工具类 EmailCodeUtils

        工具类内部要实现发送信息到目标邮箱+验证码生成方法 

一、发送到目标邮箱方法:

private static void sendEmail(String recipientEmail, String subject, String content) {
        String senderEmail = "你自己的邮箱"; // 发件人邮箱地址
        String senderPassword = "你的授权码"; // 发件人邮箱密码或授权码

        // 设置邮件服务器属性,我这里是网易邮箱,你们用其他邮箱的可以去搜搜对应的属性
        Properties properties = new Properties();
        properties.put("mail.transport.protocol", "smtp");// 连接协议
        properties.put("mail.smtp.host", "smtp.163.com"); // 邮件服务器主机地址,根据你的邮箱配置填写
        properties.put("mail.smtp.port", "465"); // 邮件服务器端口号,网易的SSL端口号,经过SSL加密
        properties.put("mail.smtp.auth", "true"); // 启用身份验证
        properties.put("mail.smtp.starttls.enable", "true"); // 启用TLS加密
        properties.put("mail.smtp.ssl.enable", "true");// 设置是否使用ssl安全连接 ---一般都使用
        properties.put("mail.debug", "true");// 设置是否显示debug信息 true 会在控制台显示相关信息


        // 创建会话
        Session session = Session.getInstance(properties, new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(senderEmail, senderPassword);
            }
        });

        try {
            // 创建邮件对象
            Message message = new MimeMessage(session);
            message.setFrom(new InternetAddress(senderEmail));
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipientEmail));
            message.setSubject(subject);
            message.setText(content);

            // 发送邮件
            Transport.send(message);
            System.out.println("邮件已发送成功!");
        } catch (MessagingException e) {
            e.printStackTrace();
        }
    }

        上述代码中,通过JavaMail的会话对象Session完成了与邮箱的身份校验,注意这里可能一些邮箱服务器不支持账号密码登录,所以要去邮箱网站获取授权码代替密码,来完成身份验证 

网易邮箱为例:

        1.确保开通smtp服务 

        2. 添加授权码用于身份校验

 二、生成验证码方法(可以随便写,主要是满足需求即可)

        我这里就是生成一个随机的六位验证码 

    private static String generateVerificationCode() {
        // 生成6位随机验证码
        Random random = new Random();
        StringBuilder verificationCode = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            verificationCode.append(random.nextInt(10));
        }
        return verificationCode.toString();
    }

 2.完成Redis的配置

一、到官网下载Redis的下载压缩包 (github网站)

 ​

二、完成Redis的一些配置类

        配置Redis的配置 ,包括访问端口,库号,超时时间,这里的host和port我是从application.yml获取的,也可以直接赋值给他们,其中host="localhost" port="6379"

@Configuration
@ConfigurationProperties(prefix = "spring.redis")
@Data
public class RedisConfig {
    private String host;
    private String port;
    private Integer database;

    @Bean
    public RedissonClient redisson(){
        // 创建配置
        Config config = new Config();
        String redisAddress="redis://"+host+":"+port;
        System.out.println(redisAddress);
        //setdatebase是指redis库号
        config.useSingleServer().setAddress(redisAddress).setDatabase(database).setTimeout(3000);
        //创建实例
        RedissonClient redisson = Redisson.create(config);
        return redisson;
    }

}

         规定Redis的存储类型(泛型),键值对的形式,我们写入读取都是以键值对的形式(通过key找value),然后就是一些初始化配置

@Configuration
public class RedisTemplateConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate <String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        redisTemplate.setKeySerializer(RedisSerializer.string());
        return redisTemplate;
    }
}

3.完成Redis存储和读取数据 

         我下面给了我服务层的发送验证码的代码,需要注意的就是通过@Resource注入RedisTemplate,存储 键值对数据(redisKey 和 UserDto) 到Redis中,后面我们就可以通过 redisKey找到我们的数据 UserDto类的userDto

 完整代码如下:(这里我在写入缓存之后,用日志输出了一下,方便debug)

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Override
    public String sendMsg(String mail) {
        //前端设置了非空,所以不需要判断非空其实,这里是为了postman测试用
        if (!StringUtils.isNotEmpty(mail))
            throw new CustomException("email必须非空");
        String code = EmailCodeUtil.send(mail);
        UserDto userDto = new UserDto(mail, code);
        log.info("成功发出邮件,验证码应为:{}", code);

        //用Redis存储
        // 作为唯一标识
        String redisKey = String.format("my:user:sendMessage:%s", mail);

        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        // 写缓存
        try {
            valueOperations.set(redisKey, userDto, 300000, TimeUnit.MILLISECONDS);
            UserDto sendMessage = (UserDto) valueOperations.get(redisKey);
            log.info(sendMessage.toString());
            return "成功发出验证码,请在五分钟内使用";
        } catch (Exception e) {
            log.error("redis set key error", e);
            throw new CustomException("缓存失败!");
        }
    }

表现层对应代码:

4.登陆验证和发放jwt令牌 

        大致流程:从前端请求体中获取用户输入的邮箱和验证码 ——>从Redis中获取到存储的UserDto对象,拿出存的邮箱和验证码——>保证邮箱和验证码同时相等,才允许接下来的操作——>如果是新用户就加入到数据库表User中,老用户直接下发jwt令牌作为登陆凭证

服务层代码: 

@Override
    public String VerifyCode(UserDto userDto) {
        //获取email
        String mail = userDto.getMail();
        String code = userDto.getCode();
        //要传回去的token,初始为 ""
        String token=null;
        //从redsson获取验证码
        String redisKey = String.format("my:user:sendMessage:%s", mail);
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        log.info(redisKey);
        //通过key获取之前存在redis中的UserSendMessage
        UserDto sendedMessage = (UserDto) valueOperations.get(redisKey);

        //如果这两个都符合redis存储的email和code,那就说明验证码接收正确,允许登录,发放token,并判断是否是新用户,是的话插入
        if (sendedMessage.getMail().equals(mail) && sendedMessage.getCode().equals(code)) {
            /*细节:如果表中已经有了这个邮箱说明是已存在的用户,就不用插入了*/
            //1.先查询表中有无该邮箱
            LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
            lqw.eq(User::getMail, mail);
            Integer count = userMapper.selectCount(lqw);

            //如果count=0,说明这是新用户,插入表中
            if (count == 0) {
                log.info("新用户,插入数据库");
                User user = new User();
                //设置邮箱值,因为在数据库里只有id和邮箱是非空约束,而id是雪花算法生成,所以直接插入就行
                user.setMail(mail);
                //插入user表
                userMapper.insert(user);
            }
            //2.发放Token,老用户直接发放token
            Map<String,Object> claims =new HashMap<>();
            //获取id
            Long id = userMapper.selectOne(lqw).getId();
            claims.put("mail",mail);
            claims.put("id",id);
            token = JwtUtils.generateJwt(claims);
            log.info("发放token:{}",token);
        }
        return token;

    }

表现层代码为: (其实可以在服务层中通过校验邮箱和验证码,不符合直接抛异常给全局异常处理器,然后返回前端error的响应就行,这样就可以让表现层的代码更简洁)

         总结:

                这次邮箱发送验证码的开发,其实起因是因为短信服务用不了(大部分云服务厂商,都不对个人提供短信服务,好像是运营商的要求),所以我转而使用邮箱发送验证码(正好省钱) ,不过这次邮箱发送验证码让我收益良多,学会了JavaMail的基础使用,和初步认识了Redis,并且简单运用了Redis缓存来保存数据与读取数据

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值