SpringBoot接入腾讯云实现前后端验证码过期实现

本文详细讲解如何使用SpringBoot与腾讯云短信服务配合,实现实时验证码的生成、存储与过期检查。通过控制全局变量和时间戳策略,确保多用户同时请求时验证码的正确验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringBoot接入腾讯云实现前后端验证码实现

实现效果

实现两个按钮,一个获取验证码按钮的接口,一个就是登录的接口

效果里面的验证码我设置了10秒过期

在这里插入图片描述

基础方法的实现(时间相减来设置过期时间)

讲讲原理:

这是我是根据发送短信的接口生成了验证码s,将s这个变量设置为该Controller层的全局变量,用于后面登录接口的比较。

然后为了保证手机和发送短信时的间对应,我有设置了map的全局变量,构成了手机为key与发送短信的时间为value值的映射。

在登录接口:根据手机号key,得到短信创建时间value值,比对登录的时的当前时间,判断是否过期。然后再比较验证码。

问题

效果是实现了,但是当多个用户同时访问的时候,这个全局验证码的值会不会也会跟着变化啊?比较是不是会不匹配啊?

解决方法

还是根据map来吧,创建Map<String, <类> map,根据key值为电话号码,value值为一个类实体类,实体类里面有创建时间和验证码。

1.添加依赖

<!--        腾讯云短信依赖-->
        <dependency>
            <groupId>com.tencentcloudapi</groupId>
            <artifactId>tencentcloud-sdk-java</artifactId>
            <version>3.1.270</version><!-- 注:这里只是示例版本号(可直接使用),可获取并替换为 最新的版本号,注意不要使用4.0.x版本(非最新版本) -->
        </dependency>

2.直接写Controller层就好

@RestController
@CrossOrigin
public class smsController {

    @Autowired
    UserService userService;

    //随机生成验证码
    private String s = "";
    //得到当前调用时间
    private Long createTime = null;
    //存电话号码和时间
    Map<String, Long> map = new HashMap<>();

    //这里有个Result类,可能没有,你可以改为你自己的或者,直接将返回值改为String也行
    @GetMapping("send")
    public Result sendMessage(String phone) {
        try {
            // 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密
            // 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取
            //拿自己的密钥
            Credential cred = new Credential("****************", "***************");
            // 实例化一个http选项,可选的,没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint("sms.tencentcloudapi.com");
            // 实例化一个client选项,可选的,没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象,clientProfile是可选的
            SmsClient client = new SmsClient(cred, "ap-guangzhou", clientProfile);
            // 实例化一个请求对象,每个接口都会对应一个request对象
            SendSmsRequest req = new SendSmsRequest();
            //电话号码,可继续添加
            String[] phoneNumberSet1 = {phone};
            List<String> list = new ArrayList<>();
            for (String s1 : phoneNumberSet1) {
                list.add(s1);
            }

            req.setPhoneNumberSet(phoneNumberSet1);
            //系统应用ID 不可少
            req.setSmsSdkAppId("1400556240");
            //签名内容 不可少  对应的是最开始头
            req.setSignName("专注于java学习");
            //模板id
            req.setTemplateId("1069287");

            s = CodeUtiles.generateOne();
            createTime = System.currentTimeMillis();

            map.put(list.get(0),createTime);

            //验证码 参数{1},如果还有参数就继续往后添加即可
            String[] templateParamSet1 = {s};
            System.out.println("s:"+s);
            req.setTemplateParamSet(templateParamSet1);
            // 返回的resp是一个SendSmsResponse的实例,与请求对象对应
//            SendSmsResponse resp = client.SendSms(req);
            // 输出json格式的字符串回包
//            System.out.println(SendSmsResponse.toJsonString(resp));
            return Result.ok("发送成功,验证码为"+s);
        } catch (Exception e) {
            System.out.println(e.toString());
        }
        return Result.error("发送失败");
    }

    @PostMapping("phoneLogin")
    public R login(@RequestParam(value = "code",required = false ) String code,
                   @RequestParam(value = "phone",required = false ) String phone){
        //登录的时间
        Long nowTime = System.currentTimeMillis();
        //开始申请的时间
        Long createdTime = map.get(phone);
        //判断该电话号码是否申请了验证码
        if (createdTime==null){
            return  R.error().message("验证码不存在");
        }
        //超过30秒,验证码过期,方便测试
        if (nowTime - createdTime < 1000*30) {
            if (code.equals(s)) {
//            System.out.println("s::::"+s);
//            UserVo userVo = userService.findUserPhone(phone);
//            System.out.println("result:"+userVo);
//            if (userVo != null){
//                String token = JwtUtilstwo.getJwtToken(userVo.getUserName());
//                System.out.println("token:"+token);
//                return R.ok().data("token",token);
//            }
                return R.ok().data("验证码为", s);
            }
        }
        return  R.error().message("验证码已过期,请重新申请");
    }
}

3.验证码生成

public class CodeUtiles {
    private static final Logger log = Logger.getLogger("CodeUtiles.class");
    /**
     * 预约码码表
     */
//    private static final char[] CODE_TABLE= {'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'};

    private static final char[] CODE_TABLE = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

    /**
     * 随机码默认位数-- 6位
     */
    private static final int DEFAULT_DIGIT = 6;

    /**
     * 默认随机码个数-- 2个
     */
    private static final int DEFAULT_NUMBER = 2;

    private CodeUtiles() {
    }

    /**
     * 默认生成单个6位预约码
     */
//    @Scheduled(cron = "30 * * * * ? *")
    public static String generateOne() {
        StringBuilder strBuild = new StringBuilder();
        for (int i = 0; i < DEFAULT_DIGIT; i++) {
//            strBuild.append(CODE_TABLE[new Random().nextInt(36)]);
            strBuild.append(CODE_TABLE[new Random().nextInt(10)]);
        }
        return strBuild.toString();
    }

    /**
     * 指定位数生成随机码,位数需大于0,否则返回默认6位随机码
     *
     * @param digit 随机码的位数
     */
    public static String generateOne(int digit) {
        if (digit > 0) {
            StringBuilder strBuild = new StringBuilder();
            for (int i = 0; i <= digit; i++) {
//                strBuild.append(CODE_TABLE[new Random().nextInt(36)]);
                strBuild.append(CODE_TABLE[new Random().nextInt(10)]);
            }
            return strBuild.toString();
        }
        return generateOne();
    }

    /**
     * 默认生成 2个 6位随机码
     */
    public static List<String> generateList() {
        List<String> randomCodes = new ArrayList<>(DEFAULT_NUMBER);
        for (int i = 0; i < DEFAULT_NUMBER; i++) {
            randomCodes.add(generateOne());
        }
        return randomCodes;
    }

    /**
     * 指定生成随机码个数、位数生成随机码,位数需大于0,否则返回默认6位随机码,个数需大于1,否则默认2个
     *
     * @param number 随机码生成个数
     * @param digit  随机码位数
     */
    public static List<String> generateList(int number, int digit) {
        if (number > 1) {
            List<String> randomCodes = new ArrayList<>(number);
            for (int i = 0; i < number; i++) {
                randomCodes.add(generateOne(digit));
            }
            return randomCodes;
        }
        return generateList();
    }

    /**
     * 测试入口
     */
    public static void main(String[] args) {
        log.info("循环开始" + System.currentTimeMillis());
        for (int i = 0; i < 10; i++) {
            System.out.println(generateOne());
        }
        log.info("循环结束" + System.currentTimeMillis());
    }
}


Redis自动设置过去时间

原理

其实这个原理没啥好讲的,都被redis给封装好了,直接调用redis模板给你提供的方法就可以了。

感兴趣的还可以去看看源码啥的。

这里主要是还是用到了对map的存取过程。

1.添加依赖

<!--        Spring data redis 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2.添加配置文件

Redis:
      # 超时时间
      timeout: 10000ms
    # 服务器地址
      host: 127.0.0.1
    # 服务器端口
      port: 6379
    # 数据库
      database: 0
      #密码
      password:
      lettuce:
        pool:
          # 最大连接数,默认8
          max-active: 1024
          # 最大连接阻塞等待时间 ,默认-1
          max-wait: 10000ms
          #最大空闲连接
          max-idle: 200
          #最小空闲连接
          min-idle: 5

3.Controller层直接注入

	@Autowired
    private RedisTemplate redisTemplate;

4.Controller层逻辑代码

//参数1:是key 键
            //参数2:是值 是存入redis里的值
            //参数3:时间,类型为long
            //参数4:时间类型,
            //如:TimeUnit.MILLISECONDS 代表毫秒
            //TimeUnit.SECONDS 代表秒,还有天,周,月,年自己测试  过期时间为120秒
            //这里时间要设置好,太快了像那种几百毫秒就不要了,反应不过来,会报错
redisTemplate.opsForValue().set(list.get(0), s, 100, TimeUnit.SECONDS);
//根据key值得到value值  --这里只是进行同一个方法的测试,下面那个才是真的测试
String phoneCode = redisTemplate.opsForValue().get(phone).toString();

5.Controller层的测试接口

@PostMapping("redisPhoneLogin")
    public R redisPhoneLogin(@RequestParam(value = "code", required = false) String code,
                             @RequestParam(value = "phone", required = false) String phone) {
        String phoneCode =  redisTemplate.opsForValue().get(phone).toString();
        if (phoneCode == null) {
            return R.error().message("验证码不存在");
        }
        if (code.equals(phoneCode)) {
            return R.ok().data("验证码为", s);
        }
        return R.error().message("验证码已过期,请重新申请");
    }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值