Springboot+redis+榛子云短信验证服务进行登录

Springboot+redis+榛子云短信验证服务进行登录


前言

最近在做尚医通的医院预约挂号管理系统时,做到了使用阿里云的短信验证服务,但是现在阿里云的短信验证服务审核听说非常严格,主要是我没有专门的域名,无法通过审核,而腾讯云又又又太贵啦,50RMB起卖,因此我选择到了较为合适的榛子云平台(20RMB–>540条短信),个人测试是完全够用了,这里附带上榛子云平台的链接以及操作指南平台

登录平台
文档平台

废话说完了,抓紧进入正题,使用Springboot+redis+榛子云的登录验证服务

登录流程说明

在编译代码前,我们需要先清楚正常的登录流程是怎么样的

  1. 用户在前端页面输入手机号,然后点击发送验证码,前端对用户的手机号校验后传到后台系统
  2. 后台系统接收到用户的手机号,然后随机生成验证码
  3. 后台系统将接收到的验证码发送通过云服务平台发送短信给用户,告知它的验证码是多少
  4. 与此同时,后台发送完验证码后将(用户手机号,用户的验证码)存入redis,并设置过期时间
  5. 用户在过期时间之内登录

当然啦,中间还会涉及到用户登录中的token等操作,上面仅是整理登录过程中的思路,下面进入到操作实战部分!

操作步骤

验证码发送模块

1.引入依赖

	
    <dependencies>
        
        <dependency>
            <groupId>com.zhenzikjgroupId>
            <artifactId>zhenzismsartifactId>
            <version>2.0.2version>
        dependency>
    dependencies>

2.编写配置文件(application.properties)

# 服务端口
server.port=8204
# 服务名
spring.application.name=service-msm

#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0

# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

sms.apiUrl=https://sms_developer.zhenzikj.com
#下图给说明
sms.appId=个人的appId
sms.appSecret=个人的密钥

Springboot+redis+榛子云短信验证服务进行登录_第1张图片

3.短信配置类

package com.hang.msm.config;

import com.zhenzi.sms.ZhenziSmsClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

@Configuration
public class SmsConfig {
    @Value("${sms.apiUrl}")
    private String apiUrl;

    @Value("${sms.appId}")
    private String appId;

    @Value("${sms.appSecret}")
    private String appSecret;

    public String printRandom(){
        //取随机产生的验证码(4位数字)
        Random rnd = new Random();
        int randNum = rnd.nextInt(8999) + 1000;
        //将整型数字转化成字符串
        String randStr = String.valueOf(randNum);
        return randStr;
    }

    public String sendMessage(String randNum,String phoneNum) throws Exception {
        ZhenziSmsClient client = new ZhenziSmsClient(apiUrl, appId, appSecret);
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("number", phoneNum);
        params.put("templateId","9915");        //短信模板ID,在个人中心的短信模板中查看
        String[] templateParams = new String[2];
        templateParams[0] = randNum;
        templateParams[1] = "5分钟";
        params.put("templateParams", templateParams);
        return client.send(params);
    }
}

4.对外接口控制器类

package com.hang.msm.controller;

import com.hang.common.result.Result;
import com.hang.msm.config.SmsConfig;
import com.hang.msm.service.MsmService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/api/msm")
public class MsmApiController {
    @Autowired
    private MsmService msmService;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private SmsConfig smsConfig;

    //发送手机验证码
    /**业务逻辑
     *  1.从redis中获取验证码,如果可以获取到,那么就返回结果
     *  2.如果无法获取,那么就生成验证码,然后通过短信服务发送
     *  3.生成的验证码放入的redis中,并且生成对应的过期时间
     */
    @GetMapping("send/{phone}")
    public Result sendCode(@PathVariable("phone") String phone){
        //key:phone value-->code
        String code = redisTemplate.opsForValue().get(phone);
        if(!StringUtils.isEmpty(code)){
            //验证码不为空,返回结果即可
            return Result.ok();
        }

        //如果是空,那么生成验证码并发送
        String random = smsConfig.printRandom();

        boolean sendSuccess = msmService.send(phone,random);

        if(sendSuccess){
            //发送成功(需要设置有效时间:5min)
            redisTemplate.opsForValue().set(phone,random,5, TimeUnit.MINUTES);
            return Result.ok();
        }else {
            //发送失败
            return Result.fail().message("发送短信失败");
        }
    }
}

4.编写对应的服务接口和实现类

package com.hang.msm.service.impl;


import com.hang.msm.config.SmsConfig;
import com.hang.msm.service.MsmService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

@Service
public class MsmServiceImpl implements MsmService {
    @Autowired
    private SmsConfig smsConfig;

    @Override
    public boolean send(String phone, String code) {
        //判断手机号是否为空
        if(StringUtils.isEmpty(phone)){
            return false;
        }

        //整合榛子云的短信服务发送
        try {
            smsConfig.sendMessage(code,phone);
        } catch (Exception e) {
            //打印相对应的异常
            e.printStackTrace();
            return false;
        }
        return true;
    }
}

至此,短信发送的服务就编写完毕,然后在登录模块中对其进行调用即可

登录模块

登录模块中我就不多赘述了,就是获取到前端填写的验证码和redis中存取的验证码进行校验登录,并且返回对应的token信息,需要详尽的代码的可以发留言后面有时间了发布出来

package com.hang.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hang.common.exception.YyghException;
import com.hang.common.helper.JwtHelper;
import com.hang.common.result.ResultCodeEnum;
import com.hang.model.user.UserInfo;
import com.hang.user.mapper.UserInfoMapper;
import com.hang.user.service.UserInfoService;
import com.hang.vo.user.LoginVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;

@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    /***登录方法
     *
     * @param loginVo
     * @return
     */
    @Override
    public Map<String, Object> loginUser(LoginVo loginVo) {
        //获取手机号和验证码
        String phone = loginVo.getPhone();
        String code = loginVo.getCode();

        //先从loginVo中获取手机号和验证码是否为空
        if(StringUtils.isEmpty(phone)||StringUtils.isEmpty(code)){
            //抛出对应的异常
            throw new YyghException(ResultCodeEnum.PARAM_ERROR);
        }

        //验证码是否正确
        String redisCode = redisTemplate.opsForValue().get(phone);
        if(!code.equals(redisCode)){
            //验证码不正确
            throw new YyghException(ResultCodeEnum.CODE_ERROR);
        }

        //用户是否注册
        QueryWrapper<UserInfo> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("phone",phone);
        UserInfo userInfo = baseMapper.selectOne(queryWrapper);
        if(userInfo==null){
            //用户未注册,进行用户注册(应该返回信息通知用户进入注册界面进行注册)
            userInfo = new UserInfo();
            //这里简单进行注册
            userInfo.setName("");
            userInfo.setPhone(phone);
            userInfo.setStatus(1);

            baseMapper.insert(userInfo);
        }

        //状态校验
        //先判断用户是否正常
        if(userInfo.getStatus()==0){
            throw new YyghException(ResultCodeEnum.LOGIN_DISABLED_ERROR);
        }

        Map<String,Object> map =new HashMap<>();
        String name = userInfo.getName();
        //用户名为空先去调真实名,真实名仍为空调用手机号
        if(StringUtils.isEmpty(name)){
            name = userInfo.getNickName();
        }
        if(StringUtils.isEmpty(name)){
            name = userInfo.getPhone();
        }

        map.put("name",name);


        //登录信息返回(登录信息,登录用户名,token)


        /*什么是token
         *  token放在请求头中,每次请求的时候可以对请求头中的token进行校验
         *  如果token校验成功,那么表示已经登录并且具有权限访问,反之则拒绝访问
         *  同样地,我们可以设置token的过期时间,过期后即失效
         *
         *  token具有一定的规则,一般可以结合JWT工具进行生成
         *
         * JWT一般由三部分组成:
         *    JWT头
         *      声明类型
         *      使用的加密算法
         *    载荷信息
         *      存放有效信息
         *    签名
         *      base64之后的头和载荷信息,以及密钥secret组成
         *
         * 将签名得到的编码再根据使用的加密算法进行加密,从而得到jwt
         */

        //生成token信息,并且返回
        String token = JwtHelper.createToken(userInfo.getId(), name);
        map.put("token",token);
        return map;
    }
}

最后前端调用这些接口即可进行登录
可以在榛子云的控制台中查看短信服务使用情况
Springboot+redis+榛子云短信验证服务进行登录_第2张图片

总结

不拘泥于解决方案的束缚,尝试使用其它解决方案去解决问题,也是人生的一门必修课,加油~

你可能感兴趣的:(项目实战,redis,spring,boot,java)