图形验证码生成及短信验证码发送流程及相关代码

前提:首先我们的页面上有个图形验证码 然后有个短信验证码 有个点击发送按钮

每生成一次图形验证码都会放入缓存  后面发送短信验证码的请求 中的请求体必须包含它

其它短信发送相关bean见 :短信发送相关代码(使用了异步+线程池+http连接池 优化)

下面有俩接口 一个生成图形验证码 一个发送短信验证码 

package net.xdclass.controller;

import com.google.code.kaptcha.Producer;
import lombok.extern.slf4j.Slf4j;
import net.xdclass.constant.RedisConstant;
import net.xdclass.controller.request.SendCodeRequest;
import net.xdclass.enums.BizCodeEnum;
import net.xdclass.enums.SendCodeEnum;
import net.xdclass.service.NotifyService;
import net.xdclass.util.CommonUtil;
import net.xdclass.util.JsonData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/api/v1/account/notify")
@Slf4j
public class NotifyController {


    @Autowired
    private Producer captchaProducer;


    @Autowired
    private NotifyService notifyService;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 生成验证码
     *
     * @param request
     * @param response
     */
    @GetMapping("captcha")
    public void getCaptcha(HttpServletRequest request, HttpServletResponse response) {

        String captchaText = captchaProducer.createText();
        log.info("验证码内容:{}", captchaText);

        //存储redis,配置过期时间
        String captchaKey = getCaptchaKey(request);
        redisTemplate.opsForValue().set(captchaKey, captchaText, RedisConstant.CAPTCHA_KEY_TIMEOUT, TimeUnit.MILLISECONDS);

        BufferedImage bufferedImage = captchaProducer.createImage(captchaText);

        try {
            ServletOutputStream outputStream = response.getOutputStream();
            ImageIO.write(bufferedImage, "jpg", outputStream);
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            log.error("获取流出错:{}", e.getMessage());
        }

    }

    private String getCaptchaKey(HttpServletRequest request) {

        String ip = CommonUtil.getIpAddr(request);
        String userAgent = request.getHeader("User-Agent");
        String captchaKey = "account-service:captcha:" + CommonUtil.MD5(ip + userAgent);

        return captchaKey;
    }


    /**
     * 测试发送验证码接口-主要是用于对比优化前后区别
     *
     * @return
     */
    @PostMapping("send_code")
    public JsonData sendCode(@RequestBody SendCodeRequest sendCodeRequest, HttpServletRequest request) {

        String captchaKey = getCaptchaKey(request);
        String cacheCaptchaValue = redisTemplate.opsForValue().get(captchaKey).toString();

        String captcha = sendCodeRequest.getCaptcha();
        String to = sendCodeRequest.getTo();

        if (null != cacheCaptchaValue && null != captcha && captcha.equals(cacheCaptchaValue)) {
            redisTemplate.delete(captchaKey);
            JsonData result = notifyService.sendCode(SendCodeEnum.USER_REGISTER,to);
            return result;
        } else {
            return JsonData.buildResult(BizCodeEnum.CODE_CAPTCHA_ERROR);
        }

    }

}

短信验证码请求体

@Data
public class SendCodeRequest {

    private String captcha;

    private String to;
}

为了防刷 短信验证码 60秒只能发一次  所以

以 前缀+to(to就是发送手机号) 为key 以验证码+时间戳为value

 存入redis 来进行校验 

package net.xdclass.service.impl;

import lombok.extern.slf4j.Slf4j;
import net.xdclass.component.SmsComponent;
import net.xdclass.config.SmsConfig;
import net.xdclass.constant.RedisConstant;
import net.xdclass.constant.RedisKey;
import net.xdclass.enums.BizCodeEnum;
import net.xdclass.enums.SendCodeEnum;
import net.xdclass.service.NotifyService;
import net.xdclass.util.CheckUtil;
import net.xdclass.util.CommonUtil;
import net.xdclass.util.JsonData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * 小滴课堂,愿景:让技术不再难学
 *
 * @Description
 * @Author 二当家小D
 * @Remark 有问题直接联系我,源码-笔记-技术交流群
 * @Version 1.0
 **/

@Service
@Slf4j
public class NotifyServiceImpl implements NotifyService {


    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private SmsComponent smsComponent;

    @Autowired
    private SmsConfig smsConfig;

    @Override
    public JsonData sendCode(SendCodeEnum sendCodeEnum, String to) {
        try {
            //判断是否重复发送 以防刷 60秒内只能一次
            String cacheKey = String.format(RedisKey.CHECK_CODE_KEY, SendCodeEnum.USER_REGISTER, to);
            String cacheValue = redisTemplate.opsForValue().get(cacheKey);

            if (cacheValue != null) {
                long leftTime = CommonUtil.getCurrentTimestamp() - Long.valueOf(cacheValue.split("_")[1]);
                if (leftTime < 5 * 60 * 1000) {
                    return JsonData.buildResult(BizCodeEnum.CODE_LIMITED);
                }
            }

            //若是超过一分钟则 重新把验证码存入缓存 并且发送
            String sendCode = CommonUtil.getRandomCode(6);
            cacheValue = sendCode + "_" + CommonUtil.getCurrentTimestamp();
            redisTemplate.opsForValue().set(cacheKey, cacheValue, RedisConstant.CAPTCHA_KEY_TIMEOUT, TimeUnit.MILLISECONDS);

            if (CheckUtil.isEmail(to)) {
                //todo
                return JsonData.buildSuccess();
            } else if (CheckUtil.isPhone(to)) {
                smsComponent.send(to, smsConfig.getTemplateId(), sendCode);
                log.info("发送短信验证码成功");
                return JsonData.buildSuccess();
            }

        } catch (Exception e) {
            String errorMsg = "发送短信验证码失败";
            log.info(errorMsg);
            return JsonData.buildError(errorMsg);
        }
        return null;
    }
}

相关枚举类 与常量类

public class RedisKey {

/**
* 验证码缓存key,第一个是类型,第二个是唯一标识比如手机号或者邮箱
*/
public static final String CHECK_CODE_KEY = "code:%s:%s";


}
public enum SendCodeEnum {

/**
* 用户注册
*/
USER_REGISTER;

}
import lombok.Getter;


public enum BizCodeEnum {


/**
* 短链分组
*/
GROUP_REPEAT(23001,"分组名重复"),
GROUP_OPER_FAIL(23503,"分组名操作失败"),
GROUP_NOT_EXIST(23404,"分组不存在"),


/**
*验证码
*/
CODE_TO_ERROR(240001,"接收号码不合规"),
CODE_LIMITED(240002,"验证码发送过快"),
CODE_ERROR(240003,"验证码错误"),
CODE_CAPTCHA_ERROR(240101,"图形验证码错误"),



/**
* 账号
*/
ACCOUNT_REPEAT(250001,"账号已经存在"),
ACCOUNT_UNREGISTER(250002,"账号不存在"),
ACCOUNT_PWD_ERROR(250003,"账号或者密码错误"),
ACCOUNT_UNLOGIN(250004,"账号未登录"),


/**
* 短链
*/
SHORT_LINK_NOT_EXIST(260404,"短链不存在"),


/**
* 订单
*/
ORDER_CONFIRM_PRICE_FAIL(280002,"创建订单-验价失败"),
ORDER_CONFIRM_REPEAT(280008,"订单恶意-重复提交"),
ORDER_CONFIRM_TOKEN_EQUAL_FAIL(280009,"订单令牌缺少"),
ORDER_CONFIRM_NOT_EXIST(280010,"订单不存在"),

/**
* 支付
*/
PAY_ORDER_FAIL(300001,"创建支付订单失败"),
PAY_ORDER_CALLBACK_SIGN_FAIL(300002,"支付订单回调验证签失败"),
PAY_ORDER_CALLBACK_NOT_SUCCESS(300003,"支付宝回调更新订单失败"),
PAY_ORDER_NOT_EXIST(300005,"订单不存在"),
PAY_ORDER_STATE_ERROR(300006,"订单状态不正常"),
PAY_ORDER_PAY_TIMEOUT(300007,"订单支付超时"),


/**
* 流控操作
*/
CONTROL_FLOW(500101,"限流控制"),
CONTROL_DEGRADE(500201,"降级控制"),
CONTROL_AUTH(500301,"认证控制"),


/**
* 流量包操作
*/
TRAFFIC_FREE_NOT_EXIST(600101,"免费流量包不存在,联系客服"),

TRAFFIC_REDUCE_FAIL(600102,"流量不足,扣减失败"),

TRAFFIC_EXCEPTION(600103,"流量包数据异常,用户无流量包"),


/**
* 通用操作码
*/

OPS_REPEAT(110001,"重复操作"),
OPS_NETWORK_ADDRESS_ERROR(110002,"网络地址错误"),


/**
* 文件相关
*/
FILE_UPLOAD_USER_IMG_FAIL(700101,"用户头像文件上传失败");

@Getter
private String message;

@Getter
private int code;

private BizCodeEnum(int code, String message){
this.code = code;
this.message = message;
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JsonData {

/**
* 状态码 0 表示成功
*/

private Integer code;
/**
* 数据
*/
private Object data;
/**
* 描述
*/
private String msg;


/**
* 获取远程调用数据
* 注意事项:
* 支持多单词下划线专驼峰(序列化和反序列化)
*
* @param typeReference
* @param <T>
* @return
*/
public <T> T getData(TypeReference<T> typeReference){
return JSON.parseObject(JSON.toJSONString(data),typeReference);
}

/**
* 成功,不传入数据
* @return
*/
public static JsonData buildSuccess() {
return new JsonData(0, null, null);
}

/**
* 成功,传入数据
* @param data
* @return
*/
public static JsonData buildSuccess(Object data) {
return new JsonData(0, data, null);
}

/**
* 失败,传入描述信息
* @param msg
* @return
*/
public static JsonData buildError(String msg) {
return new JsonData(-1, null, msg);
}



/**
* 自定义状态码和错误信息
* @param code
* @param msg
* @return
*/
public static JsonData buildCodeAndMsg(int code, String msg) {
return new JsonData(code, null, msg);
}

/**
* 传入枚举,返回信息
* @param codeEnum
* @return
*/
public static JsonData buildResult(BizCodeEnum codeEnum){
return JsonData.buildCodeAndMsg(codeEnum.getCode(),codeEnum.getMessage());
}
}

 


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我才是真的封不觉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值