04_2023_Redis_redis实现短信发送

1,基于session实现

1.1 流程图

在这里插入图片描述

1.1 pom

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

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

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.8.0</version>
        </dependency>

        <!--Redis依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

1.2 配置文件

server:
  port: 18083

spring:
  application:
    name: springboot_redis
  datasource:
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
    lettuce:
      pool:
        max-active: 8 #最大连接数
        max-idle: 8 #最大空闲连接
        min-idle: 0 #最小空闲连接
        #连接等待时间
        max-wait: 100

1.3 启动类

package com.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.study")
public class RedisSmsApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedisSmsApplication.class,args);
    }
}

1.4 完成拦截器

package com.study.intercept;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("preHandle");
        //1.获取session
        HttpSession session = request.getSession();
        //2.获取session中的用户
        Object user = session.getAttribute("user");
        //3.判断用户是否存在. 不存在:拦截;存在:放入ThreadLocal,放行(写了ThreadLocal的封装工具类UserHolder)
        if(user==null){
            response.setStatus(401);
            response.getWriter().write("user not login!");
            return false;
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion");
    }
}

1.5 完成拦截器config

package com.study.config;

import com.study.intercept.LoginInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@Slf4j
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        log.info("addInterceptors---");
        registry.addInterceptor(new LoginInterceptor())
                .excludePathPatterns(
                        "/smsSessionController/demo1",
                        "/smsSessionController/login",
                        "/smsSessionController/getSessionCode",
                        "/smsSessionController/sendCode"
                );
    }
}

1.6 完成工具类

  • idsutil
/*
 * 文 件 名:IdsUtil.java
 * 系统名称:风险管控平台
 * Copyright@2003-2019 State Grid Corporation of China, All Rights Reserved
 * 版本信息:V1.0
 * 版   权:NARI
 */

package com.study.utils;

import java.security.SecureRandom;
import java.util.UUID;

/**
 * 概述:id生成工具类
 * 功能:
 * 作者:15657
 * 创建时间:2019-05-29 14:20
 */
public class IdsUtil {

    public static String[] chars = new String[]{"a", "b", "c", "d", "e", "f",
            "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
            "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
            "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
            "W", "X", "Y", "Z"};

    /**
     * 生成8位uuid
     *
     * @return
     */
    public static String getUUID8() {
        StringBuffer shortBuffer = new StringBuffer();

        String uuid = UUID.randomUUID().toString().replace("-", "");

        for (int i = 0; i < 8; i++) {
            String str = uuid.substring(i * 4, i * 4 + 4);
            int x = Integer.parseInt(str, 16);
            shortBuffer.append(chars[x % 0x3E]);
        }

        return shortBuffer.toString();
    }

    /**
     * 生成16位uuid
     *
     * @return
     */
    public static String getUUID16() {
        String uuid = UUID.randomUUID().toString();
        char[] cs = new char[32];
        char c = 0;
        for (int i = uuid.length() / 2, j = 1; i --> 0;) {
            if ((c = uuid.charAt(i)) != '-') {
                cs[j++] = c;
            }
        }
        String uid = String.valueOf(cs);
        return uid.trim();
    }

    /**
     * 生成32位uuid
     *
     * @return
     */
    public static String getUUID32() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    //生成指定长度字符串
    public static String getRandomString(int length){
        String base="0123456789";
        SecureRandom random = new SecureRandom();
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<length;i++){
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
}

  • RegexUtils
package com.study.utils;

import java.util.regex.Pattern;

public class RegexUtils {
    private RegexUtils() {
    }

    /**
     * 验证电话号码
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isTel(CharSequence input) {
        return estimate("^0\\d{2,3}[- ]?\\d{7,8}", input);
    }

    /**
     * 验证身份证号码18位
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isIDCard18(CharSequence input) {
        return estimate("^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9Xx])$", input);
    }

    /**
     * 验证手机号(简单)
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isMobileSimple(CharSequence input) {
        return estimate("^[1]\\d{10}$", input);
    }

    /**
     * 验证手机号(精确)
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isMobileExact(CharSequence input) {
        return estimate("^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|(147))\\d{8}$", input);
    }


    /**
     * 验证邮箱
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isEmail(CharSequence input) {
        return estimate("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", input);
    }

    /**
     * 验证URL
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isURL(CharSequence input) {
        return estimate("[a-zA-z]+://[^\\s]*", input);
    }

    /**
     * 验证汉字
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isChinesec(CharSequence input) {
        return estimate("^[\\u4e00-\\u9fa5]+$", input);
    }

    /**
     * 验证yyyy-MM-dd格式的日期校验
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isDate(CharSequence input) {
        return estimate("^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$", input);
    }

    /**
     * 验证IP地址
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isIP(CharSequence input) {
        return estimate("((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)", input);
    }

    /**
     * 判断是否匹配正则
     *
     * @param regex 正则表达式
     * @param input 要匹配的字符串
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean estimate(String regex, CharSequence input) {
        return input != null && input.length() > 0 && Pattern.matches(regex, input);
    }
}

1.7 完成实体类

  • LoginFormDTO
package com.study.entity;

import lombok.Data;

@Data
public class LoginFormDTO {
    private String phone;
    private String code;
}

  • ResponseResult
package com.study.entity;


import lombok.Data;

import java.io.Serializable;


@Data
public class ResponseResult<T> implements Serializable {
    private Boolean success;//是否成功
    private Integer code;//状态码
    private String message;//返回消息
    private T data;

    public ResponseResult() {
    }

    public static <T> ResponseResult<T> ok(){
        ResponseResult<T> responseResult = new ResponseResult<T>();
        responseResult.setSuccess(true);
        responseResult.setCode(ResultCode.SUCCESS);
        responseResult.setMessage("执行成功");
        return responseResult;
    }

    public static <T> ResponseResult<T> ok(T data){
        ResponseResult<T> responseResult = new ResponseResult<T>();
        responseResult.setSuccess(true);
        responseResult.setCode(ResultCode.SUCCESS);
        responseResult.setMessage("执行成功");
        responseResult.setData(data);
        return responseResult;
    }

    public static <T> ResponseResult<T> error(Integer code){
        ResponseResult<T> responseResult = new ResponseResult<T>();
        responseResult.setSuccess(false);
        responseResult.setCode(code);
        responseResult.setMessage("执行失败");
        return responseResult;
    }
    public static <T> ResponseResult<T> error(){
        ResponseResult<T> responseResult = new ResponseResult<T>();
        responseResult.setSuccess(false);
        responseResult.setCode(ResultCode.ERROR);
        responseResult.setMessage("执行失败");
        return responseResult;
    }

    public ResponseResult<T> success(Boolean success){
        this.setSuccess(success);
        return this;
    }

    public ResponseResult<T> code (Integer code){
        this.setCode(code);
        return this;
    }

    public ResponseResult<T> message (String message){
        this.setMessage(message);
        return this;
    }

    public static<T> ResponseResult<T> exist (String message){
        ResponseResult<T> responseResult = new ResponseResult<T>();
        responseResult.setSuccess(true);
        responseResult.setCode(ResultCode.SUCCESS);
        responseResult.setMessage(message);
        responseResult.setSuccess(true);
        return responseResult;
    }

    public static<T> ResponseResult<T> exist (){
        ResponseResult<T> responseResult = new ResponseResult<T>();
        responseResult.setSuccess(true);
        responseResult.setCode(ResultCode.SUCCESS);
        responseResult.setSuccess(true);
        return responseResult;
    }
}

  • ResultCode
package com.study.entity;

public class ResultCode {
    public static final Integer SUCCESS = 200;
    public static final Integer ERROR = 500;
    public static final int NOT_LOGIN = 600;
    public static final int NOT_AUTH = 700;
}

  • User
package com.study.entity;

import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class User {
    private String id;
    private String name;
    private Integer age;
}

1.8 完成对外接口

package com.study.session.controller;

import com.study.entity.LoginFormDTO;
import com.study.entity.ResponseResult;
import com.study.entity.User;
import com.study.utils.IdsUtil;
import com.study.utils.RegexUtils;
import io.lettuce.core.dynamic.annotation.Param;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;
@Slf4j
@RestController
@RequestMapping("/smsSessionController")
public class SmsSessionController {

    private static User user;
    static {
        user = new User();
        user.setAge(25);
        user.setId(IdsUtil.getUUID8());
        user.setName("李乃龙");
    }

    /**
     * 测试接口
     * @return
     */
    @GetMapping("demo1")
    private String demo1(){
        return "demo1";
    }

    /**
     * 发送验证码
     * @param phone
     * @param session
     * @return
     */
    @GetMapping("sendCode")
    private ResponseResult sendCode(@RequestParam("phone") String phone, HttpSession session){
        //1.校验手机号:利用util下RegexUtils进行正则验证
        boolean mobileExact = RegexUtils.isMobileExact(phone);
        if(!mobileExact){
            return ResponseResult.error().message("手机号格式不正确!");
        }
        //2.生成验证码:导入hutool依赖,内有RandomUtil
        String code = IdsUtil.getUUID8();
        //3.保存验证码到session
        session.setAttribute(phone,code);
        //4.发送验证码
        log.info("验证码为: " + code);
        log.debug("发送短信验证码成功!");

        return ResponseResult.ok("发送短信验证码成功!验证码为: " + code);
    }

    /**
     * 获取验证码
     * @param session
     * @return
     */
    @GetMapping("getSessionCode")
    private ResponseResult getSessionCode(@RequestParam("phone") String phone,HttpSession session){
        //2.生成验证码:导入hutool依赖,内有RandomUtil
        Object code = session.getAttribute(phone);
        Object user = session.getAttribute("user");
        return ResponseResult.ok("发送短信验证码成功!验证码为: " + code+",用户信息为:"+user);
    }

    /**
     * 登录
     * @param loginForm
     * @param session
     * @return
     */
    @PostMapping("login")
    private ResponseResult login(@RequestBody LoginFormDTO loginForm, HttpSession session){
        //1.校验手机号
        String phone = loginForm.getPhone();
        if(!RegexUtils.isMobileExact(phone)){
            return ResponseResult.error().message("手机号格式不正确!");
        }
        //2.校验验证码
        Object cacheCode = session.getAttribute(phone);
        String code = loginForm.getCode();
        if(code==null||!cacheCode.toString().equals(code)){
            //3.不一致,报错
            return ResponseResult.error().message("验证码错误!");
        }

        //6.存入session
        session.setAttribute("user",user.toString());
        return ResponseResult.ok().message("登录成功!");
    }
}

1.9 测试

  • http://localhost:18083/smsSessionController/sendCode?phone=18110229299
    在这里插入图片描述

  • http://localhost:18083/smsSessionController/getSessionCode?phone=18110229299
    在这里插入图片描述

  • http://localhost:18083/smsSessionController/login
    在这里插入图片描述

1.10 集群的session共享问题

多台Tomcat不共享session存储空间,当请求切换到不同的tomcat服务时导致数据丢失的问题

所以我们把数据存入Redis,集群的Redis可以替代session

2,基于redis实现

2.1 pom

  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

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

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.8.0</version>
        </dependency>

        <!--Redis依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.21</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

2.2 配置文件

server:
  port: 18083

spring:
  application:
    name: springboot_redis
  datasource:
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
    lettuce:
      pool:
        max-active: 8 #最大连接数
        max-idle: 8 #最大空闲连接
        min-idle: 0 #最小空闲连接
        #连接等待时间
        max-wait: 100



2.3 启动类

package com.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.study")
public class RedisSmsApplication {
    public static void main(String[] args) {
        SpringApplication.run(RedisSmsApplication.class,args);
    }
}

2.4 拦截器

package com.study.interceptor;

import com.study.constants.RedisConstants;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class LoginInterceptor implements HandlerInterceptor {
    private final StringRedisTemplate stringRedisTemplate;

    public LoginInterceptor(StringRedisTemplate stringRedisTemplate){
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.获取请求头中的token
        String token = request.getHeader("authorization");
        if (StringUtils.isEmpty(token)){
            //不存在,拦截 设置响应状态吗为401(未授权)
            response.setStatus(401);
            return false;
        }
        //2.基于token获取redis中用户
        String key= RedisConstants.LOGIN_USER_KEY + token;
        Map<Object, Object> userMap = stringRedisTemplate.opsForHash().entries(key);
        //3.判断用户是否存在
        if (userMap.isEmpty()){
            //4.不存在则拦截,设置响应状态吗为401(未授权)
            response.setStatus(401);
            return false;
        }

        //7.更新token的有效时间,只要用户还在访问我们就需要更新token的存活时间
        stringRedisTemplate.expire(key, RedisConstants.LOGIN_USER_TTL, TimeUnit.SECONDS);
        //8.放行
        return true;
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}

2.5 拦截器配置config

package com.study.config;

import com.study.interceptor.LoginInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor(stringRedisTemplate))
                .excludePathPatterns(
                        "/smsRedisController/demo1",
                        "/smsRedisController/login",
                        "/smsRedisController/getSessionCode",
                        "/smsRedisController/sendCode"
                );
    }
}

2.6 完成工具类

  • IdsUtil
/*
 * 文 件 名:IdsUtil.java
 * 系统名称:风险管控平台
 * Copyright@2003-2019 State Grid Corporation of China, All Rights Reserved
 * 版本信息:V1.0
 * 版   权:NARI
 */

package com.study.utils;

import java.security.SecureRandom;
import java.util.UUID;

/**
 * 概述:id生成工具类
 * 功能:
 * 作者:15657
 * 创建时间:2019-05-29 14:20
 */
public class IdsUtil {

    public static String[] chars = new String[]{"a", "b", "c", "d", "e", "f",
            "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
            "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
            "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
            "W", "X", "Y", "Z"};

    /**
     * 生成8位uuid
     *
     * @return
     */
    public static String getUUID8() {
        StringBuffer shortBuffer = new StringBuffer();

        String uuid = UUID.randomUUID().toString().replace("-", "");

        for (int i = 0; i < 8; i++) {
            String str = uuid.substring(i * 4, i * 4 + 4);
            int x = Integer.parseInt(str, 16);
            shortBuffer.append(chars[x % 0x3E]);
        }

        return shortBuffer.toString();
    }

    /**
     * 生成16位uuid
     *
     * @return
     */
    public static String getUUID16() {
        String uuid = UUID.randomUUID().toString();
        char[] cs = new char[32];
        char c = 0;
        for (int i = uuid.length() / 2, j = 1; i --> 0;) {
            if ((c = uuid.charAt(i)) != '-') {
                cs[j++] = c;
            }
        }
        String uid = String.valueOf(cs);
        return uid.trim();
    }

    /**
     * 生成32位uuid
     *
     * @return
     */
    public static String getUUID32() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    //生成指定长度字符串
    public static String getRandomString(int length){
        String base="0123456789";
        SecureRandom random = new SecureRandom();
        StringBuffer sb = new StringBuffer();
        for(int i=0;i<length;i++){
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }
}

  • RegexUtils
package com.study.utils;

import java.util.regex.Pattern;

public class RegexUtils {
    private RegexUtils() {
    }

    /**
     * 验证电话号码
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isTel(CharSequence input) {
        return estimate("^0\\d{2,3}[- ]?\\d{7,8}", input);
    }

    /**
     * 验证身份证号码18位
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isIDCard18(CharSequence input) {
        return estimate("^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9Xx])$", input);
    }

    /**
     * 验证手机号(简单)
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isMobileSimple(CharSequence input) {
        return estimate("^[1]\\d{10}$", input);
    }

    /**
     * 验证手机号(精确)
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isMobileExact(CharSequence input) {
        return estimate("^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|(147))\\d{8}$", input);
    }


    /**
     * 验证邮箱
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isEmail(CharSequence input) {
        return estimate("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", input);
    }

    /**
     * 验证URL
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isURL(CharSequence input) {
        return estimate("[a-zA-z]+://[^\\s]*", input);
    }

    /**
     * 验证汉字
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isChinesec(CharSequence input) {
        return estimate("^[\\u4e00-\\u9fa5]+$", input);
    }

    /**
     * 验证yyyy-MM-dd格式的日期校验
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isDate(CharSequence input) {
        return estimate("^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$", input);
    }

    /**
     * 验证IP地址
     *
     * @param input 待验证文本
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean isIP(CharSequence input) {
        return estimate("((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)", input);
    }

    /**
     * 判断是否匹配正则
     *
     * @param regex 正则表达式
     * @param input 要匹配的字符串
     * @return {@code true}: 匹配<br>{@code false}: 不匹配
     */
    public static boolean estimate(String regex, CharSequence input) {
        return input != null && input.length() > 0 && Pattern.matches(regex, input);
    }
}

2.7 完成常量类

package com.study.constants;

public class RedisConstants {
    public static final String LOGIN_CODE_KEY = "login:code:";
    public static final Long LOGIN_CODE_TTL = 10L;
    public static final String LOGIN_USER_KEY = "login:token:";
    public static final Long LOGIN_USER_TTL = 36000L;

    public static final Long CACHE_NULL_TTL = 2L;

    public static final Long CACHE_SHOP_TTL = 30L;
    public static final String CACHE_SHOP_KEY = "cache:shop:";

    public static final String LOCK_SHOP_KEY = "lock:shop:";
    public static final Long LOCK_SHOP_TTL = 10L;

    public static final String SECKILL_STOCK_KEY = "seckill:stock:";
    public static final String BLOG_LIKED_KEY = "blog:liked:";
    public static final String FEED_KEY = "feed:";
    public static final String SHOP_GEO_KEY = "shop:geo:";
    public static final String USER_SIGN_KEY = "sign:";
}

2.8 完成实体类

  • LoginFormDTO
package com.study.entity;

import lombok.Data;

@Data
public class LoginFormDTO {
    private String phone;
    private String code;
}

  • ResponseResult
package com.study.entity;


import lombok.Data;

import java.io.Serializable;


@Data
public class ResponseResult<T> implements Serializable {
    private Boolean success;//是否成功
    private Integer code;//状态码
    private String message;//返回消息
    private T data;

    public ResponseResult() {
    }

    public static <T> ResponseResult<T> ok(){
        ResponseResult<T> responseResult = new ResponseResult<T>();
        responseResult.setSuccess(true);
        responseResult.setCode(ResultCode.SUCCESS);
        responseResult.setMessage("执行成功");
        return responseResult;
    }

    public static <T> ResponseResult<T> ok(T data){
        ResponseResult<T> responseResult = new ResponseResult<T>();
        responseResult.setSuccess(true);
        responseResult.setCode(ResultCode.SUCCESS);
        responseResult.setMessage("执行成功");
        responseResult.setData(data);
        return responseResult;
    }

    public static <T> ResponseResult<T> error(Integer code){
        ResponseResult<T> responseResult = new ResponseResult<T>();
        responseResult.setSuccess(false);
        responseResult.setCode(code);
        responseResult.setMessage("执行失败");
        return responseResult;
    }
    public static <T> ResponseResult<T> error(){
        ResponseResult<T> responseResult = new ResponseResult<T>();
        responseResult.setSuccess(false);
        responseResult.setCode(ResultCode.ERROR);
        responseResult.setMessage("执行失败");
        return responseResult;
    }

    public ResponseResult<T> success(Boolean success){
        this.setSuccess(success);
        return this;
    }

    public ResponseResult<T> code (Integer code){
        this.setCode(code);
        return this;
    }

    public ResponseResult<T> message (String message){
        this.setMessage(message);
        return this;
    }

    public static<T> ResponseResult<T> exist (String message){
        ResponseResult<T> responseResult = new ResponseResult<T>();
        responseResult.setSuccess(true);
        responseResult.setCode(ResultCode.SUCCESS);
        responseResult.setMessage(message);
        responseResult.setSuccess(true);
        return responseResult;
    }

    public static<T> ResponseResult<T> exist (){
        ResponseResult<T> responseResult = new ResponseResult<T>();
        responseResult.setSuccess(true);
        responseResult.setCode(ResultCode.SUCCESS);
        responseResult.setSuccess(true);
        return responseResult;
    }
}

  • ResultCode
package com.study.entity;

public class ResultCode {
    public static final Integer SUCCESS = 200;
    public static final Integer ERROR = 500;
    public static final int NOT_LOGIN = 600;
    public static final int NOT_AUTH = 700;
}

  • User
package com.study.entity;

import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class User {
    private String id;
    private String name;
    private Integer age;
}

2.9 完成对外接口

package com.study.controller;

import cn.hutool.core.lang.UUID;
import com.study.constants.RedisConstants;
import com.study.entity.LoginFormDTO;
import com.study.entity.ResponseResult;
import com.study.entity.User;
import com.study.utils.IdsUtil;
import com.study.utils.RegexUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
@RequestMapping("/smsRedisController")
public class SmsRedisController {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private static User user;
    static {
        user = new User();
        user.setAge(25);
        user.setId(IdsUtil.getUUID8());
        user.setName("李乃龙");
    }

    /**
     * 测试接口
     * @return
     */
    @GetMapping("demo1")
    private String demo1(){
        return "demo1";
    }

    /**
     * 发送验证码
     * @param phone
     * @param session
     * @return
     */
    @GetMapping("sendCode")
    private ResponseResult sendCode(@RequestParam("phone") String phone, HttpSession session){
        //1.校验手机号:利用util下RegexUtils进行正则验证
        boolean mobileExact = RegexUtils.isMobileExact(phone);
        if(!mobileExact){
            return ResponseResult.error().message("手机号格式不正确!");
        }
        //2.生成验证码:导入hutool依赖,内有RandomUtil
        String code = IdsUtil.getUUID8();
        //3.保存验证码到session
        stringRedisTemplate.opsForValue().set(RedisConstants.LOGIN_CODE_KEY +phone,code, RedisConstants.LOGIN_CODE_TTL, TimeUnit.MINUTES);//有效期2mins
        //4.发送验证码
        log.info("验证码为: " + code);
        log.debug("发送短信验证码成功!");

        return ResponseResult.ok("发送短信验证码成功!验证码为: " + code);
    }

    /**
     * 获取验证码
     * @param session
     * @return
     */
    @GetMapping("getSessionCode")
    private ResponseResult getSessionCode(@RequestParam("phone") String phone,
                                          @RequestParam("tokenKey") String tokenKey,
                                          HttpSession session){
//        //2.生成验证码:导入hutool依赖,内有RandomUtil
        String code = stringRedisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + phone);
        String user = stringRedisTemplate.opsForValue().get(tokenKey);
        return ResponseResult.ok("发送短信验证码成功!验证码为: " + code+",用户信息为:"+user);
    }

    /**
     * 登录
     * @param loginForm
     * @param session
     * @return
     */
    @PostMapping("login")
    private ResponseResult login(@RequestBody LoginFormDTO loginForm, HttpSession session){
        //1.校验手机号
        String phone = loginForm.getPhone();
        if(!RegexUtils.isMobileExact(phone)){
            return ResponseResult.error().message("手机号格式不正确!");
        }

        //2.校验验证码
        String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstants.LOGIN_CODE_KEY + phone);
        String code = loginForm.getCode();

        if(code==null||!cacheCode.toString().equals(code)){
            //3.不一致,报错
            return ResponseResult.error().message("验证码错误!");
        }

        //生成token
        String token = UUID.randomUUID().toString(true);//hutools
        //存储到Redis的key
        String tokenKey = RedisConstants.LOGIN_USER_KEY + token;

        //User转为HashMap存储
        HashMap<Object, Object> userMap = new HashMap<>();
        userMap.put("id", user.getId());
        userMap.put("name", user.getName());
        userMap.put("age", user.getAge());

        //存储到Redis
        stringRedisTemplate.opsForValue().set(tokenKey,userMap.toString());
        //设置有效期
        stringRedisTemplate.expire(tokenKey,RedisConstants.LOGIN_USER_TTL,TimeUnit.MINUTES);

        return ResponseResult.ok().message("登录成功! tokenKey:"+tokenKey);
    }
}

2.10 测试

  • http://localhost:18083/smsRedisController/sendCode?phone=18110229299

在这里插入图片描述

  • http://localhost:18083/smsRedisController/getSessionCode?phone=18110229299&tokenKey=login:token:12e812bd6f654dbfb71ff47b352ad222

在这里插入图片描述

  • http://localhost:18083/smsRedisController/login
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值