单车第六天

转自http://coder520.com/
1、发送短信
1.1、设置常量
1.2、对接短信是一个https的接口,所以我们需要在代码中发送https请求,我们使用httpclient包来操作。
1.3、创建HttpcUtil工具类
1.4、由于我们的发短信接口,只要是一个电话我们就发一条短信,存在安全问题,如果不法分子通过抓包知道我们的短信接口,然后每天不断的请求我们的接口,我们都得发送短信,一条一分这样不好。
短信接口安全性解决办法:
1、IP, 通过ip来确定,超过十次,不发验证码
2、手机号,同一个手机号在一定时间内不能不停请求如果超过一定的次数,就不发验证码,这个记录次数没必要存到数据库,存到redis就行。
1.5、

@RequestMapping(value = "/sendVercode")
    public ApiResult<String> sendVercode(@RequestBody User user, HttpServletRequest request) {

        ApiResult<String> resp = new ApiResult();

        try {
            userService.sendVercode(user.getMobile(), getIpFromRequest(request));
        } catch (MaMaBikeException e) {
            //发送失败
            log.error(e.getMessage());
            resp.setCode(Constants.RESP_STATUS_INTERNAL_ERROR);
            resp.setMessage(e.getMessage());
        } catch (Exception e) {
            // 登录失败,返回失败信息,就不用返回data
            // 记录日志
            log.error("Fail to send smsVercode", e);
            resp.setCode(Constants.RESP_STATUS_INTERNAL_ERROR);
            resp.setMessage("内部错误!");
        }
        return resp;
    }

1.6、发送短信的业务层
1、调用产生验证码方法

/**1、生成4位随机验证码**/
String verCode = RandomNumberCode.verCode();
/**
 * Author ljs
 * Description 生成随机验证码
 * Date 2018/10/2 3:00
 **/
public class RandomNumberCode {

    public static String verCode(){
        Random random =new Random();
        /**每次生成太长我们去第2到第6位**/
        return StringUtils.substring(String.valueOf(random.nextInt()*-10), 2, 6);
    }
}

2、把验证码存到redis

  • 需要存的三对
    key1(手机号) value(验证码)
    key2(ip) value(次数)
    key3(手机号+类型) value(次数)

  • 1 当前验证码未过期 2 手机号超过当前验证码次数上限 3、ip超过当日验证码上线 0 开始发送验证码短信

  • 我们要想当发送验证码的时候我们需要存啥到redis里,第一个手机号一定要的吧(作为key),第二个验证码也要,第三个过期时间也要,第四个它的ip也要(安全问题),第五个是类型,是为了表明这是注册时候发送的验证码,如果以后还有其他地方需要发送验证码可以区分一下。之前写的redis的操作类没有这种类型的存储方法,自己创建一个

  • 接着要想想它的逻辑,
    第一当然是获取redis链接。。。。。
    2、当获取redis链接成功后,先判断传过来的ip是否为null,为null返回3,不为null,接着判断缓存中key2的存储的次数,如果大于10就返回3,如果都没问题,我们就开始存储key3,使用setnx,如果key不存在存储,存在(之前的验证码还没有过期)不存储。
    3、接着获取redis中的key3并且判断是否超过次数,如果不超过就开始设置之前存的key1的过期时间为60秒,并且key3的值+1并且返回,如果key3是第一次存储(手机号第一次注册),我们就设置key3的过期时间为一天,并且如果key2也是第一次存储,我们也设置key2的过期时间为一天。
    4、最后都没有报错,存储设置成功,返回0表示可以开始发送验证码短信。

/**
     * Author ljs
     * Description 缓存手机验证码专用 限制了发送次数
     *
     * @return 1 当前验证码未过期   2  手机号超过当前验证码次数上限  3、ip超过当日验证码上线
     * Date 2018/10/2 15:57
     **/
    public int cacheForVerificationCode(String key, String verCode, String type, int second, String ip) throws MaMaBikeException {

        try {
            JedisPool pool = jedisPoolWrapper.getJedisPool();
            if (pool != null) {
                try (Jedis jedis = pool.getResource()) {
                    jedis.select(0);
                    /**ip次数判断**/
                    String ipKey = "ip." + ip;
                    if (ip == null) {
                        return 3;
                    } else {
                        String ipSendCount = jedis.get(ipKey);
                        try {
                            if (ipSendCount != null && Integer.parseInt(ipSendCount) >= 10) {
                                return 3;
                            }
                        } catch (NumberFormatException e) {
                            log.error("Fail to process ip send count", e);
                            return 3;
                        }
                    }

                    /**验证码存储,被设置了返回0**/
                    long succ = jedis.setnx(key, verCode);
                    if (succ == 0) {
                        return 1;
                    }


                    /**手机号次数判断**/
                    String phoneCountKey = key + "." + type;
                    String sendCount = jedis.get(phoneCountKey);
                    try {
                        if (sendCount != null && Integer.parseInt(sendCount) >= 10) {
                            jedis.del(phoneCountKey);
                            return 2;
                        }
                    } catch (NumberFormatException e) {
                        log.error("Fail to process send count", e);
                        jedis.del(key);
                        return 2;
                    }


                    /**都没问题之后给三个key设置过期时间并且value+1**/
                    try {
                        jedis.expire(key, second);
                        long val = jedis.incr(ipKey);
                        /**该ip是第一次存储**/
                        if (val == 1) {
                            jedis.expire(=ipKey, 86400);
                        }

                        val = jedis.incr(phoneCountKey);
                        if (val == 1) {
                            jedis.expire(phoneCountKey, 86400);
                        }
                    }catch (Exception e){
                        log.error("Fail to cache data into redis", e);
                    }

                }
            }
        } catch (Exception e) {
            log.error("Fail to cache for expiry", e);
            throw new MaMaBikeException("Fail to cache for expiry");
        }

        return 0;
    }

3、写好redis存储之后,接着只需要在service判断1,2,3就行了

/**2、存到redis**/
        int result = redis.cacheForVerificationCode(VERIFYCODE_PREFIX+mobile,verCode,"reg",60,ip);
        if(result==1){
            log.error("当前验证码未过期,请稍后重试");
            throw new MaMaBikeException("当前验证码未过期,请稍后重试");

        }else if(result==2){
            log.error("超过当日验证码次数上限");
            throw new MaMaBikeException("超过当日验证码次数上限");

        }else if(result==3){
            log.error("超过当日验证码次数上限{}", ip);
            throw new MaMaBikeException(ip + "超过当日验证码次数上限");

        }

4、在发送之前,需要一个发送短信的工具类,为了以后如果更换其他平台的短信服务,我们可以弄个接口进行解耦。
5、接下来就开始调用发送短信的工具类了,但是由于发短信和我们本身的业务没有关系,而且它成不成功和我们后续逻辑也没有关系,抛异常再发一次的都是秒滴那边的事,我们也并不依赖它返回的结果,不管它发没发成功。所以我们不在service层直接调用工具类来发送短信,我们需要进行解耦,这时候就可以使用mq来把这个东西搞出去(异步),进行解耦。

6、整合mq
6.1、pom

<!--整合mq-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
        </dependency>

6.2、因为这也是个第三方组件,我们把mq配置写到dev.xml里,而不是主配置里

 #activeMQ
  activemq:
     broker-url: tcp://localhost:61616
     pool:
       enabled: false

6.3、启动mq
6.4、

/**
 * Author ljs
 * Description mq处理器,既可以发送短信又可以监听发送短信的队列
 * Date 2018/10/3 11:13
 **/
@Component(value = "smsProcessor")
public class SmsProcessor {

    @Autowired
    private JmsMessagingTemplate jmsTemplate;

    @Autowired
    @Qualifier("verCodeService")
    private SmsSender smsSender;

    public void sendSmsToQueue(Destination destination, final String message){
        jmsTemplate.convertAndSend(destination, message);
    }

    @JmsListener(destination="sms.queue")
    public void doSendSmsMessage(String text){
        JSONObject jsonObject = JSON.parseObject(text);
        smsSender.sendSms(jsonObject.getString("mobile"),jsonObject.getString("tplId"),jsonObject.getString("vercode"));
    }

}

service

  //验证码推送到队列
        Destination destination = new ActiveMQQueue(SMS_QUEUE);
        Map<String,String> smsParam = new HashMap<>();
        smsParam.put("mobile",mobile);
        smsParam.put("tplId", Constants.MDSMS_VERCODE_TPLID);
        smsParam.put("vercode",verCode);
        String message = JSON.toJSONString(smsParam);
        sms.sendSmsToQueue(destination,message);

记得在paramater里加上sendVercode,因为这是一个无需验证的url。之后debug,


image.png

body里把号码发过去就行了,最后手机也收到验证码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,884评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,212评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,351评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,412评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,438评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,127评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,714评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,636评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,173评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,264评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,402评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,073评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,763评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,253评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,382评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,749评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,403评论 2 358

推荐阅读更多精彩内容

  • 文章已经放到github上 ,如果对您有帮助 请给个star[https://github.com/qqxuanl...
    尼尔君阅读 2,287评论 0 22
  • 【本教程目录】 1.redis是什么2.redis的作者3.谁在使用redis4.学会安装redis5.学会启动r...
    徐猿猿阅读 1,870评论 0 35
  • NOSQL类型简介键值对:会使用到一个哈希表,表中有一个特定的键和一个指针指向特定的数据,如redis,volde...
    MicoCube阅读 3,992评论 2 27
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,685评论 18 139
  • 我的设置标准是参考sublime 缩进设置 四个字符的缩进setting -> editor -> 勾选soft ...
    akira_preview阅读 586评论 0 0