背景

小组我的项目要做一个邮箱找回明码的性能。 须要用到redis

大略流程如下:

  1. 第一步,用户填写邮箱,并点击“获取验证码”,浏览器发送申请,调用获取验证码接口。而后,服务端依据邮箱,生成验证码,发送验证码给这个邮箱,并将验证码和邮箱和有效期放到redis/内存中
  2. 用户在邮箱中查到验证码后→填写到登录界面→点击登录→前端申请登录接口,携带邮箱和验证码参数。
  3. 服务器收到申请后,到redis中取到这个邮箱对应的信息→校验验证码是否正确,并验证是否过期,如果验证码正确且没有过期,则失常登录。

Redis

先介绍一下redis

Redis 是齐全开源收费的,,是一个高性能的key-value非关系性数据库(NoSql)。

Redis 与其余 key - value 缓存产品有以下三个特点:

  • Redis反对数据的长久化,能够将内存中的数据保留在磁盘中,重启的时候能够再次加载进行应用。
  • Redis不仅仅反对简略的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。



Redis能够存储键与5种不同数据结构类型之间的映射,这5种数据结构类型别离为String(字符串)、List(列表)、Set(汇合)、Hash(散列)和 Zset(有序汇合)。

下载Redis

间接拜访github网址:https://github.com/MSOpenTech...,
如下图所示:

我下载的是windows版本

启动Redis






redis.windows.conf为redis配置文件,相干参数能够在这里配置,如:端口等,我这里应用默认参数,暂不批改,默认端口为6379。双击redis-server.exe启动,则呈现如下图所示,则启动胜利。

SpringBoot中应用Redis

在我的项目的pom.xml中增加如下:

<dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-data-redis</artifactId></dependency>

application.yml 中配置如下

  # redis 配置  redis:    # 地址    host: localhost    # 端口,默认为6379    port: 6379    # 数据库索引    database: 0    # 明码    password:    # 连贯超时工夫    timeout: 10s    lettuce:      pool:        # 连接池中的最小闲暇连贯        min-idle: 0        # 连接池中的最大闲暇连贯        max-idle: 8        # 连接池的最大数据库连接数        max-active: 8        # #连接池最大阻塞等待时间(应用负值示意没有限度)        max-wait: -1ms

编写Redis操作工具类

咱们对redis操作须要用到RedisTemplate

RedisTemplate 是简化 Redis 数据库拜访代码的助手类,也是 Spring Data Redis 对 Redis 反对的核心类。

咱们这里将RedisTemplate实例包装成一个工具类,便于对redis进行数据操作。

创立spring redis 工具类

@Componentpublic class RedisCache {    @Autowired    public RedisTemplate redisTemplate;        // redis序列化器 类型为string    @Autowired(required = false)    public void setRedisTemplate(RedisTemplate redisTemplate) {        RedisSerializer stringSerializer = new StringRedisSerializer();        redisTemplate.setKeySerializer(stringSerializer);        redisTemplate.setValueSerializer(stringSerializer);        redisTemplate.setHashKeySerializer(stringSerializer);        redisTemplate.setHashValueSerializer(stringSerializer);        this.redisTemplate = redisTemplate;    }}

上面编写工具办法

1.设置缓存对象

  /**     * 缓存根本的对象,Integer、String、实体类等     *     * @param key      缓存的键值     * @param value    缓存的值     * @param timeout  工夫     * @param timeUnit 工夫颗粒度     */    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);    }

比如说咱们要将验证码存入redis,就能够这么做

// 验证码存入redis并设置过期工夫redisCache.setCacheObject("mail_code:" + username, verifyCode, 5, TimeUnit.MINUTES);

这里咱们设置

key为 mail_code: + username,示意某个用户的邮箱验证码

value为 verifycode , 示意验证码

timeout为5, timeUnit为 MINUTES, 示意过期工夫为5分钟。

2.取得缓存的根本对象。

/**     * 取得缓存的根本对象。     *     * @param key 缓存键值     * @return 缓存键值对应的数据     */    public <T> T getCacheObject(final String key) {        ValueOperations<String, T> operation = redisTemplate.opsForValue();        return operation.get(key);    }

比方应用如下: 依据key 获取 value

String verifyCode = redisCache.getCacheObject("mail_code: + usrname);

3.设置无效工夫

/**     * 设置无效工夫     *     * @param key     Redis键     * @param timeout 超时工夫     * @param unit    工夫单位     * @return true=设置胜利;false=设置失败     */    public boolean expire(final String key, final long timeout, final TimeUnit unit) {        return redisTemplate.expire(key, timeout, unit);    }

比方咱们在用户胜利应用完该验证码后,想将该验证码设置为过期:

// 将验证码过期redisCache.expire("mail_code: + usrname, 0, TimeUnit.SECONDS);

示意该键值对会在0秒后过期。

4.删除对象

/** * 删除单个对象 * * @param key */public boolean deleteObject(final String key) {    return redisTemplate.delete(key);}


除这几个罕用的外,还编写了几个类,比方依据key,写入新的值,笼罩旧的值等,在此就不一一列出。

同时咱们还能够编写,对Set,Hash,Map,List 类型的操作工具类,然而我没有用到,所以没有编写。

应用邮箱发送验证码

这里用到SMTP服务

SMTP

SMTP的全称是“Simple Mail Transfer Protocol”,即简略邮件传输协定。

什么是 SMTP?
SMTP代表简略邮件传输协定,它是电子邮件发送的行业标准协议。
应用 SMTP,你能够从邮件客户端(如 Microsoft Outlook)向接管电子邮件服务器发送、中继或转发邮件。发件人将应用SMTP 服务器来执行发送电子邮件的过程。
在思考是应用 SMTP 还是 IMAP 时要记住的要害是 SMTP 是对于发送电子邮件的。因而,如果你心愿在你的应用程序中启用电子邮件发送,那么你须要持续应用 SMTP over IMAP。

什么是 IMAP?
如果 SMTP 是对于发送的,那么 IMAP 是什么?
简而言之,IMAP(Internet 拜访音讯协定)是一种电子邮件协定,用于治理和检索来自接管服务器的电子邮件音讯。
因为 IMAP 解决音讯检索,因而你将无奈应用 IMAP 协定发送电子邮件。相同,IMAP 将用于接管音讯。

除了 IMAP,还有另一种用于接管电子邮件的协定 — 称为 POP3。感兴趣能够查一下

简略来说, SMTP用于发送, IMAP用于接管

因为只发送,所以我只用到SMTP。

发件邮箱要求

这里应用的是163邮箱,

1.设置POP3/SMTP/IMAP

2.开启服务
开启会让设置受权码,受权码要记牢!前面须要写在配置里

3.查看服务器地址
再往下翻有163 SMTP服务器地址 等下须要配置在application.yml中

代码实现

1.pom依赖

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-mail</artifactId></dependency>

2.application.yml配置

  # 配置邮箱服务器,账号密码等  mail:    host: smtp.163.com    username: xxx@163.com    password: xxx    port:    default-encoding: utf-8    protocol: smtp    properties:      mail.smtp.auth: true      mail.smtp.starttls.enable: true      mail.smtp.starttls.required: true      mail.smtp.socketFactory.port: 465      mail.smtp.socketFactory.class: javax.net.ssl.SSLSocketFactory      mail.smtp.socketFactory.fallback: false

3.获取验证码controller

@RestController@RequestMapping("/mail")public class MailController {    @Autowired    private MailService mailService;    /**     * 获取重置明码的验证码     */    @GetMapping("/getCode")    public void getCode(@RequestParam String staffNumber, @RequestParam String mailAddress){         mailService.getCode(staffNumber,mailAddress);    }}

4. ServiceImpl层

@Servicepublic class MailServiceImpl implements MailService {    @Autowired    private RedisCache redisCache;    @Autowired    private JavaMailSender mailSender;    @Autowired    private UserRepository userRepository;    @Value("${spring.mail.username}")    private String mailUserName;    @Value("${mail.code.overtime}")    private Integer overtime; // 过期工夫 /**     * 获取重置明码的验证码     *     * @param username 用户账号     * @param mailAddress 用户邮箱     * @return     */    @Override    public void getCode(String username, String mailAddress) {        // 非空校验        if (null == username || "".equals(username)) throw new EntityNotFoundException("账号不能为空!");        if (null == mailAddress || "".equals(mailAddress)) throw new EntityNotFoundException("邮箱不能为空!");        // 账号存在校验        User user = userRepository.findByUsernameAndDeletedIsFalse(username);        if (null == user) throw new EntityNotFoundException("账号不存在!");        if (!user.getPhone().equals(mailAddress)) throw new EntityNotFoundException("输出邮箱和预留邮箱不统一!");          verifyCode = String.valueOf(new Random().nextInt(899999) + 100000);//生成短信验证码                // 验证码存入redis并设置过期工夫        redisCache.setCacheObject(Constants.MAIL_CODE_KEY + username, verifyCode, overtime, TimeUnit.MINUTES);        // 编写邮箱内容        StringBuilder stringBuilder = new StringBuilder();        stringBuilder.append("<html><head><title></title></head><body>");        stringBuilder.append("您好<br/>");        stringBuilder.append("您的验证码是:").append(verifyCode).append("<br/>");        stringBuilder.append("您能够复制此验证码并返回至设施管理系统找回明码页面,以验证您的邮箱。<br/>");        stringBuilder.append("此验证码只能应用一次,在");        stringBuilder.append(overtime.toString());        stringBuilder.append("分钟内无效。验证胜利则主动生效。<br/>");        stringBuilder.append("如果您没有进行上述操作,请疏忽此邮件。");        MimeMessage mimeMessage = mailSender.createMimeMessage();        // 发件配置并发送邮件        try {            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);            //这里只是设置username 并没有设置host和password,因为host和password在springboot启动创立JavaMailSender实例的时候曾经读取了            mimeMessageHelper.setFrom(mailUserName);            mimeMessageHelper.setTo(mailAddress);            mimeMessage.setSubject("邮箱验证-重置明码");            mimeMessageHelper.setText(stringBuilder.toString(), true);            mailSender.send(mimeMessage);        } catch (MessagingException e) {            e.printStackTrace();        }    }

次要逻辑:

1.数据校验
次要校验前台传入数据非空,以及user账号是否存在,输出邮箱和预留邮箱是否统一等

// 非空校验if (null == username || "".equals(username)) throw new EntityNotFoundException("账号不能为空!");if (null == mailAddress || "".equals(mailAddress)) throw new EntityNotFoundException("邮箱不能为空!");// 账号存在校验User user = userRepository.findByUsernameAndDeletedIsFalse(username);if (null == user) throw new EntityNotFoundException("账号不存在!");if (!user.getPhone().equals(mailAddress)) throw new EntityNotFoundException("输出邮箱和预留邮箱不统一!");

2.验证码存入redis
首先查看该key,是否存在于redis缓存中。

 verifyCode = String.valueOf(new Random().nextInt(899999) + 100000);//生成短信验证码// 验证码存入redis并设置过期工夫redisCache.setCacheObject(Constants.MAIL_CODE_KEY + username, verifyCode, overtime, TimeUnit.MINUTES);

3.编写邮件内容
代码如上,此处省略

4.发送并配置邮件
这里用到了JavaMailSender来发送邮件。

最晚期的时候会应用JavaMail相干api来写发送邮件的相干代码

spring推出了JavaMailSender简化了邮件发送的过程,

在之后springboot对此进行了封装就有了当初的spring-boot-starter-mail

 // 发件配置并发送邮件        try {            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);            //这里只是设置username 并没有设置host和password,因为host和password在springboot启动创立JavaMailSender实例的时候曾经读取了            mimeMessageHelper.setFrom(mailUserName);            mimeMessageHelper.setTo(mailAddress);            mimeMessage.setSubject("邮箱验证-重置明码");            mimeMessageHelper.setText(stringBuilder.toString(), true);            mailSender.send(mimeMessage);        } catch (                MessagingException e) {            e.printStackTrace();        }
  1. 验证验证码正确性

从redis中获取该验证码,若从redis中获取值为null, 则验证码曾经过期

String cacheCode = redisCache.getCacheObject(Constants.MAIL_CODE_KEY + num); // 获取缓存中该账号的验证码if (cacheCode == null) {    throw new EntityNotFoundException("验证码已过期,请从新获取!");}// 验证码正确性校验if (!cacheCode.equals(codes)) {    throw new EntityNotFoundException("验证码谬误!");}




参考
https://www.cnblogs.com/fonks...
https://www.cnblogs.com/equal...
https://ost.51cto.com/posts/1262