• spring boot整合redis—邮箱验证码


    redis简介

            Redis 是一个高性能的开源的、C语言写的Nosql(非关系型数据库),数据保存在内存中。

            Redis 是以key-value形式存储,和传统的关系型数据库不一样。不一定遵循传统数据库的一些基本要求,比如 说,不遵循sql标准,事务,表结构等等,非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方 法的集合。

            Java中数据结构:String,数组,list,set map…

            Redis提供了很多的方法,可以用来存取各种数据结构的数据

    什么是NoSql

            NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,它泛指非关系型的数据库。随着互联网2003年之后 web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并发的交友类型的web2.0纯动 态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅 速的发展。

    为什么用redis

            1.数据保存在内存,存取速度快,并发能力强

            2.它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、 zset(sorted set --有序集合)和 hash(哈希类型)。

            3.redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库(如 MySQL)起到很好的补充作用。

            4.它提供了Java,C/C++,C#,PHP,JavaScript等客户端,使用很方便。

            5.Redis支持集群(主从同步)。数据可以主服务器向任意数量从的从服务器上同步,从服务器可以是关联其他从 服务器的主服务器。

            6.支持持久化,可以将数据保存在硬盘的文件中

    Redis使用场景

            1:缓存,毫无疑问这是Redis当今最为人熟知的使用场景。再提升服务器性能方面非常有效。

            2:排行榜,在使用传统的关系型数据库(mysql oracle 等)来做这个事儿,非常的麻烦,而利用Redis的 SortSet(有序集合)数据结构能够简单的搞定。

            3:利用Redis中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等,这类操作如果用MySQL,频 繁的读写会带来相当大的压力;限速器比较典型的使用场景是限制某个用户访问某个API的频率,常用的有抢购 时,防止用户疯狂点击带来不必要的压力;

            4: 一些频繁被访问的数据,经常被访问的数据如果放在关系型数据库,每次查询的开销都会很大,而放在redis 中,因为redis 是放在内存中的可以很高效的访问

    spring boot整合redis—邮箱验证码

    一、springboot整合redis 环境搭建

    1 导入依赖

    1. !-- !redis-->
    2. <dependency>
    3. <groupId>org.springframework.bootgroupId>
    4. <artifactId>spring-boot-starter-data-redisartifactId>
    5. dependency>

    2 加入配置

    1. #=======redis连接池配置============== #
    2. # Redis数据库索引(默认为0)
    3. spring.redis.database=0
    4. # Redis服务器地址
    5. spring.redis.host=127.0.0.1
    6. # Redis服务器连接端口
    7. spring.redis.port=6379
    8. # Redis服务器连接密码(默认为空)
    9. spring.redis.password=
    10. # 连接池最大连接数(使用负值表示没有限制)
    11. spring.redis.jedis.pool.max-active=20
    12. # 连接池最大阻塞等待时间(使用负值表示没有限制)
    13. spring.redis.jedis.pool.max-wait=-1
    14. # 连接池中的最大空闲连接
    15. spring.redis.jedis.pool.max-idle=10
    16. # 连接池中的最小空闲连接
    17. spring.redis.jedis.pool.min-idle=0
    18. # 连接超时时间(毫秒)
    19. spring.redis.timeout=5000

    3 创建redis配置类RedisConfig

    1. @Configuration
    2. @EnableCaching // 启用缓存,使用 Lettuce,自动注入配置的方式
    3. @AutoConfigureAfter(RedisAutoConfiguration.class)
    4. public class RedisConfig extends CachingConfigurerSupport {
    5. //缓存管理器
    6. @Bean
    7. @SuppressWarnings("all")
    8. public CacheManager cacheManager(LettuceConnectionFactory factory) {
    9. RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
    10. RedisSerializationContext.SerializationPair pair =
    11. RedisSerializationContext.SerializationPair.fromSerializer(
    12. new Jackson2JsonRedisSerializer(Object.class));
    13. RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
    14. return new RedisCacheManager(writer, config);
    15. }
    16. @Bean
    17. public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
    18. RedisTemplate redisTemplate = new RedisTemplate<>();
    19. redisTemplate.setConnectionFactory(connectionFactory);
    20. // String 序列化方式
    21. StringRedisSerializer stringSerializer = new StringRedisSerializer();
    22. // 使用Jackson2JsonRedisSerialize替换默认序列化
    23. Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
    24. Jackson2JsonRedisSerializer(Object.class);
    25. ObjectMapper objectMapper = new ObjectMapper();
    26. objectMapper.setVisibility(PropertyAccessor.ALL,
    27. JsonAutoDetect.Visibility.ANY);
    28. objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    29. jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
    30. // 设置key的序列化规则
    31. redisTemplate.setKeySerializer(stringSerializer);
    32. redisTemplate.setHashKeySerializer(stringSerializer);
    33. // 设置value的序列化规则
    34. redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    35. redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
    36. return redisTemplate;
    37. }
    38. // 重新定义缓存 key 的生成策略
    39. @Bean
    40. @Override
    41. public KeyGenerator keyGenerator() {
    42. return new KeyGenerator() {
    43. @Override public Object generate(Object target, Method method, Object... params){
    44. StringBuilder sb = new StringBuilder();
    45. sb.append(target.getClass().getName());
    46. sb.append(method.getName());
    47. for(Object obj:params){
    48. sb.append(obj.toString());
    49. }
    50. return sb.toString();
    51. }
    52. };
    53. }
    54. }

    二、验证码发送和验证

    在serviceImpl中注入redis

     

    1. //注入redis
    2. @Autowired
    3. RedisTemplate redisTemplate;

    1.验证码发送

    1.1 后端,在项目-01 的基础上修改

    1.1.1   优化发送验证码方法sendCode,30-34行

    • 在验证码发送成功后,将”code:email“作为键,code作为值,并设置失效时间是1分钟
    • 将键、值、时间使用redisTemplate.opsForValue().set保存进去
    • 当失效时间到了后,redis会自动清楚这个键值。
    1. @Override
    2. public ResultVo sendCode(String email) {
    3. ResultVo rv = new ResultVo();
    4. try {
    5. //创建邮件对象
    6. SimpleMailMessage message = new SimpleMailMessage();
    7. //定义收件人邮箱
    8. message.setTo(email);
    9. //定义发件人邮箱
    10. message.setFrom(sendEmail);
    11. //定义邮件标题
    12. message.setSubject("xxx公司验证码");
    13. //定义邮件正文
    14. Random random = new Random();
    15. int code = random.nextInt(10000);
    16. message.setText("你的验证码是"+code);
    17. //发送邮件
    18. javaMailSender.send(message);
    19. System.out.println("发送成功");
    20. rv.setSuccess(true);
    21. rv.setInfo("发送成功");
    22. //发送成功放入redis
    23. //1.定义redis的键名
    24. String key = "code:"+email;
    25. //2.存入验证码并设置失效时间,分钟
    26. redisTemplate.opsForValue().set(key,code+"",1, TimeUnit.MINUTES);
    27. } catch (Exception e) {
    28. e.printStackTrace();
    29. System.out.println("发送失败");
    30. rv.setInfo("发送失败");
    31. }
    32. return rv;
    33. }

    1.2 前端,绑定按钮,并倒计时

            绑定发送验证码按钮,要做到一分钟一次请求,发送验证码后,一分钟后才能再次发送验证码

    1.2.1 绑定按钮

    1.2.2 计时器和禁用按钮实现

            重要的方法:计时器方法

    var time = setInterval(function() {}

    2.验证邮箱验证码

    2.1.后端,写实现类和控制器接口

    2.1.1.编写邮箱验证码验证方法validateCode

    • 1.先把传进来的userInfo对象的email拿出了,组成键名,然后去redis中获取改键的值
    • 2.如果值为空,则时间已经到了,失效了
    • 3.不为空,就判断传进来的userInfo对象的code是否等于redis中获取带的值,等于则输入正确,然后返回true
    • 4.否则输入错误
    1. @Override
    2. public ResultVo validateCode(UserInfo userInfo) {
    3. ResultVo rv = new ResultVo();
    4. //判断验证码是否失效
    5. //1.定义键名
    6. String key = "code:"+userInfo.getEmail();
    7. Object obj = redisTemplate.opsForValue().get(key);
    8. //2.判断这个key是否存在,如果时间有效则存在
    9. if (obj == null){
    10. rv.setInfo("验证码失效");
    11. }else {
    12. //3.验证码未失效,比较验证码
    13. if (userInfo.getCode().equals(obj)){
    14. rv.setInfo("输入正确");
    15. rv.setSuccess(true);
    16. }else {
    17. rv.setInfo("验证码输入错误");
    18. }
    19. }
    20. return rv;
    21. }

    2.1.2.写验证邮箱验证码的控制器接口

            使用的是post请求,因为要提交的是表单的数据包装的userInfo

    1. @RestController
    2. @RequestMapping("/userInfo")
    3. public class UserInfoController {
    4. //注入service
    5. @Autowired
    6. private UserInfoService userInfoService;
    7. //定义修改密码
    8. @GetMapping("/updatePwd")
    9. public ResultVo updatePwd(String email){
    10. return userInfoService.sendCode(email);
    11. }
    12. //验证输入的验证码是否失效或正确
    13. @PostMapping("/validateCode")
    14. public ResultVo validateCode(UserInfo userInfo){
    15. return userInfoService.validateCode(userInfo);
    16. }
    17. }

    2.2 前端,绑定按钮,并验证

    3.发送验证码和验证的代码

    3.1 后端

    userInfoServerImpl.java

    1. @Service
    2. public class UserInfoServiceImpl implements UserInfoService {
    3. //注入邮件发送服务类
    4. @Autowired
    5. JavaMailSender javaMailSender;
    6. @Value("${spring.mail.username}")
    7. String sendEmail;
    8. //注入redis
    9. @Autowired
    10. RedisTemplate redisTemplate;
    11. @Override
    12. public ResultVo sendCode(String email) {
    13. ResultVo rv = new ResultVo();
    14. try {
    15. //创建邮件对象
    16. SimpleMailMessage message = new SimpleMailMessage();
    17. //定义收件人邮箱
    18. message.setTo(email);
    19. //定义发件人邮箱
    20. message.setFrom(sendEmail);
    21. //定义邮件标题
    22. message.setSubject("xxx公司验证码");
    23. //定义邮件正文
    24. Random random = new Random();
    25. int code = random.nextInt(10000);
    26. message.setText("你的验证码是"+code);
    27. //发送邮件
    28. javaMailSender.send(message);
    29. System.out.println("发送成功");
    30. rv.setSuccess(true);
    31. rv.setInfo("发送成功");
    32. //发送成功放入redis
    33. //1.定义redis的键名
    34. String key = "code:"+email;
    35. //2.存入验证码并设置失效时间,分钟
    36. redisTemplate.opsForValue().set(key,code+"",1, TimeUnit.MINUTES);
    37. } catch (Exception e) {
    38. e.printStackTrace();
    39. System.out.println("发送失败");
    40. rv.setInfo("发送失败");
    41. }
    42. return rv;
    43. }
    44. @Override
    45. public ResultVo validateCode(UserInfo userInfo) {
    46. ResultVo rv = new ResultVo();
    47. //判断验证码是否失效
    48. //1.定义键名
    49. String key = "code:"+userInfo.getEmail();
    50. Object obj = redisTemplate.opsForValue().get(key);
    51. //2.判断这个key是否存在,如果时间有效则存在
    52. if (obj == null){
    53. rv.setInfo("验证码失效");
    54. }else {
    55. //3.验证码未失效,比较验证码
    56. if (userInfo.getCode().equals(obj)){
    57. rv.setInfo("输入正确");
    58. rv.setSuccess(true);
    59. }else {
    60. rv.setInfo("验证码输入错误");
    61. }
    62. }
    63. return rv;
    64. }
    65. }

    UserInfoService.java

    1. public interface UserInfoService {
    2. //邮件发送验证码
    3. ResultVo sendCode(String email);
    4. //验证验证码是否输入正确
    5. ResultVo validateCode(UserInfo userInfo);
    6. }

    UserInfoController.java

    1. @RestController
    2. @RequestMapping("/userInfo")
    3. public class UserInfoController {
    4. //注入service
    5. @Autowired
    6. private UserInfoService userInfoService;
    7. //定义修改密码
    8. @GetMapping("/updatePwd")
    9. public ResultVo updatePwd(String email){
    10. return userInfoService.sendCode(email);
    11. }
    12. //验证输入的验证码是否失效或正确
    13. @PostMapping("/validateCode")
    14. public ResultVo validateCode(UserInfo userInfo){
    15. return userInfoService.validateCode(userInfo);
    16. }
    17. }

    3.2 前端

    testEmailVaildate.vue组件

    index.js

    1. import Vue from 'vue'
    2. import Router from 'vue-router'
    3. /* 配置地址,叫组件
    4. 第一种:导入hello组件
    5. */
    6. import hello from '@/components/demo/hello'
    7. import testEmailVaildate from '../components/project/testEmailVaildate.vue'
    8. import testUpdatePwd from '../components/project/testUpdatePwd.vue'
    9. Vue.use(Router)
    10. export default new Router({
    11. // 切换到history模式
    12. mode:"history",
    13. routes: [
    14. {
    15. /* 斜杠:代表项目的默认地址 */
    16. path: '/',
    17. name: 'hello', /* 组件名称:保持名称唯一性 */
    18. component: hello /* 引入组件的别名 */
    19. },
    20. {
    21. path: '/testEmailVaildate', //*,通配所有的错误地址
    22. name: 'testEmailVaildate',
    23. component: testEmailVaildate
    24. },
    25. {
    26. path: '/testUpdatePwd', //*,通配所有的错误地址
    27. name: 'testUpdatePwd',
    28. component: testUpdatePwd
    29. },
    30. ]
    31. })

    三、修改密码

            涉及了后端数据交互、使用了全自动的框架mybatis plus,引入了shiro权限认证,以及加密工具

    1、导包

    导入了mybatis plus,shrio的依赖

    1. com.baomidou
    2. mybatis-plus-boot-starter
    3. 3.3.2
    4. mysql
    5. mysql-connector-java
    6. 5.1.17
    7. org.apache.velocity
    8. velocity-engine-core
    9. 2.2
    10. com.baomidou
    11. mybatis-plus-generator
    12. 3.3.2
    13. org.apache.shiro
    14. shiro-spring
    15. 1.2.2

    2.引入加密工具类

    1. @Component
    2. public class MDFive {
    3. /**
    4. *
    5. * @param password 要加密的密码
    6. * @param saltValue 盐值
    7. * @return
    8. */
    9. public String encrypt(String password,String saltValue){
    10. //创建一个MD5的盐值对象
    11. Object salt = new Md5Hash(saltValue);
    12. //生成加密的字符串
    13. Object result = new SimpleHash("MD5", password, salt, 1024);
    14. return result+"";
    15. }
    16. }

    3.根据user_info表完善实体类userInfo

    📎user_info.sqlicon-default.png?t=M85Bhttps://www.yuque.com/attachments/yuque/0/2022/sql/32505142/1662716539576-2cdc64e2-f912-490d-8744-b20646ac8914.sql

    1. @Data
    2. @TableName("user_info")
    3. public class UserInfo {
    4. //主键
    5. @TableId(value = "user_id",type = IdType.AUTO)
    6. private Integer userId;
    7. //用户名
    8. private String userName;
    9. //密码
    10. private String passPwd;
    11. //盐值
    12. private String salt;
    13. //邮箱
    14. private String email;
    15. //电话
    16. private String phone;
    17. //验证码
    18. @TableField(exist = false)
    19. private String code;
    20. }

    4.使用mybatis plus来接管到

    4.1.在基础包下新建dao包,并建立UserInfoDao接口

    4.2.继承BaseMapper

    1. public interface UserInfoDao extends BaseMapper {
    2. }

    4.3.在启动类上扫描dao

    1. @SpringBootApplication
    2. @MapperScan("com.hqyj.dao")
    3. public class IdeaVueApplication {
    4. public static void main(String[] args) {
    5. SpringApplication.run(IdeaVueApplication.class, args);
    6. }
    7. }

    5.写修改密码实现接口类

    1. public interface UserInfoService {
    2. //邮件发送验证码
    3. ResultVo sendCode(String email);
    4. //验证验证码是否输入正确
    5. ResultVo validateCode(UserInfo userInfo);
    6. //修改密码
    7. ResultVo updatePwd(UserInfo userInfo);
    8. }

    使用UUID来作为盐值,唯一性

    1. @Service
    2. public class UserInfoServiceImpl implements UserInfoService {
    3. //注入邮件发送服务类
    4. @Autowired
    5. JavaMailSender javaMailSender;
    6. @Value("${spring.mail.username}")
    7. String sendEmail;
    8. //注入redis
    9. @Autowired
    10. RedisTemplate redisTemplate;
    11. //注入加密工具
    12. @Autowired
    13. private MDFive mdFive;
    14. @Autowired
    15. private UserInfoDao userInfoDao;
    16. @Override
    17. public ResultVo sendCode(String email) {
    18. ResultVo rv = new ResultVo();
    19. try {
    20. //创建邮件对象
    21. SimpleMailMessage message = new SimpleMailMessage();
    22. //定义收件人邮箱
    23. message.setTo(email);
    24. //定义发件人邮箱
    25. message.setFrom(sendEmail);
    26. //定义邮件标题
    27. message.setSubject("xxx公司验证码");
    28. //定义邮件正文
    29. Random random = new Random();
    30. int code = random.nextInt(10000);
    31. message.setText("你的验证码是"+code);
    32. //发送邮件
    33. javaMailSender.send(message);
    34. System.out.println("发送成功");
    35. rv.setSuccess(true);
    36. rv.setInfo("发送成功");
    37. //发送成功放入redis
    38. //1.定义redis的键名
    39. String key = "code:"+email;
    40. //2.存入验证码并设置失效时间,分钟
    41. redisTemplate.opsForValue().set(key,code+"",1, TimeUnit.MINUTES);
    42. } catch (Exception e) {
    43. e.printStackTrace();
    44. System.out.println("发送失败");
    45. rv.setInfo("发送失败");
    46. }
    47. return rv;
    48. }
    49. @Override
    50. public ResultVo validateCode(UserInfo userInfo) {
    51. ResultVo rv = new ResultVo();
    52. //判断验证码是否失效
    53. //1.定义键名
    54. String key = "code:"+userInfo.getEmail();
    55. Object obj = redisTemplate.opsForValue().get(key);
    56. //2.判断这个key是否存在,如果时间有效则存在
    57. if (obj == null){
    58. rv.setInfo("验证码失效");
    59. }else {
    60. //3.验证码未失效,比较验证码
    61. if (userInfo.getCode().equals(obj)){
    62. rv.setInfo("输入正确");
    63. rv.setSuccess(true);
    64. }else {
    65. rv.setInfo("验证码输入错误");
    66. }
    67. }
    68. return rv;
    69. }
    70. @Override
    71. public ResultVo updatePwd(UserInfo userInfo) {
    72. ResultVo rv = new ResultVo();
    73. //生成盐值,使用UUID,生成唯一的
    74. String salt = UUID.randomUUID()+"";
    75. //记录盐值
    76. userInfo.setSalt(salt);
    77. //生成加密的字符串
    78. String pwd = mdFive.encrypt(userInfo.getUserPwd(),salt);
    79. //记录加密的密码
    80. userInfo.setUserPwd(pwd);
    81. //创建条件构造器
    82. QueryWrapper queryWrapper = new QueryWrapper<>();
    83. queryWrapper.eq("email", userInfo.getEmail());
    84. int num = userInfoDao.update(userInfo, queryWrapper);
    85. if (num > 0 ){
    86. rv.setSuccess(true);
    87. rv.setInfo("修改成功");
    88. }else {
    89. rv.setInfo("修改失败");
    90. }
    91. return rv;
    92. }
    93. }

    6.写修改密码的控制器接口

    1. @RestController
    2. @RequestMapping("/userInfo")
    3. public class UserInfoController {
    4. //注入service
    5. @Autowired
    6. private UserInfoService userInfoService;
    7. //定义修改密码
    8. @GetMapping("/updatePwd")
    9. public ResultVo updatePwd(String email){
    10. return userInfoService.sendCode(email);
    11. }
    12. //验证输入的验证码是否失效或正确
    13. @PostMapping("/validateCode")
    14. public ResultVo validateCode(UserInfo userInfo){
    15. return userInfoService.validateCode(userInfo);
    16. }
    17. //验证输入的验证码是否失效或正确
    18. @PostMapping("/updatePwd")
    19. public ResultVo updatePwd(UserInfo userInfo){
    20. return userInfoService.updatePwd(userInfo);
    21. }
    22. }

    7.测试

     

     

     

    这里前提是数据表中有当前邮箱的数据,否则找不到

  • 相关阅读:
    基于微信旅游景区景点购票小程序设计与实现 开题报告
    Unity基于EventSystem让SpriteRenderer支持点击事件
    合并区间(排序、贪心)
    正点原子 核心板IMX6ULL IIC RTC驱动 PCF8563
    【Kubernetes快速实战】
    初识RabbitMQ-概念、安装和使用
    解密Prompt7. 偏好对齐RLHF-OpenAI·DeepMind·Anthropic对比分析
    Android ViewBinding和DataBinding的几个使用方式 - 上
    HashMap 的哈希函数
    openmmlab教程3-MMSeg 使用
  • 原文地址:https://blog.csdn.net/weixin_46048259/article/details/126800242