单车

单车第六天

2018-10-03  本文已影响8人  shenyoujian

转自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

/**
     * 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里把号码发过去就行了,最后手机也收到验证码。

上一篇 下一篇

猜你喜欢

热点阅读