springboot对接第三方短信接口整合redis,ActiveMQ

1.对接第三方短信接口需要模拟一个http请求

用http-client这个包来操作。

在pom.xml文件中加入http-client的包依赖


	org.apache.httpcomponents
	httpclient
2.新建一个httputil类来模拟get/post请求
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
public class HttpUtil {
    private static final String ENCODING = "UTF-8";


    public static String post(String url, Map paramsMap) {
        CloseableHttpClient client = HttpClients.createDefault();
        String responseText = "";
        CloseableHttpResponse response = null;
        try {
            HttpPost method = new HttpPost(url);
            if (paramsMap != null) {
                List paramList = new ArrayList();
                for (Map.Entry param : paramsMap.entrySet()) {
                    NameValuePair pair = new BasicNameValuePair(param.getKey(), param.getValue());
                    paramList.add(pair);
                }
                method.setEntity(new UrlEncodedFormEntity(paramList, ENCODING));
            }
            response = client.execute(method);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                responseText = EntityUtils.toString(entity);
            }
        } catch (Exception e) {
            log.error("http request failed",e);
        } finally {
            try {
                response.close();
            } catch (Exception e) {
                log.error("",e);
            }
        }
        return responseText;
    }

    public static String get(String url, Map paramsMap) {
        CloseableHttpClient client = HttpClients.createDefault();
        String responseText = "";
        CloseableHttpResponse response = null;
        try {
            String getUrl = url+"?";
            if (paramsMap != null) {
                for (Map.Entry param : paramsMap.entrySet()) {
                    getUrl += param.getKey() + "=" + URLEncoder.encode(param.getValue(), ENCODING)+"&";
                }
            }
            HttpGet method = new HttpGet(getUrl);
            response = client.execute(method);
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                responseText = EntityUtils.toString(entity);
            }
        } catch (Exception e) {
            log.error("http request failed",e);
        } finally {
            try {
                response.close();
            } catch (Exception e) {
                log.error("",e);
            }
        }
        return responseText;
    }

}

Service接口:

public interface UserService {

    void sendVercode(String mobile, String ip) throws ustomException;
    
}

Service实现类:

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    private static final String VERIFYCODE_PREFIX = "verify.code.";

    private static final String SMS_QUEUE ="sms.queue"
    @Autowired
    private CommonCacheUtil cacheUtil;

    @Autowired
    private SmsProcessor smsProcessor;  
@Override
public void sendVercode(String mobile, String ip) throws CustomException {
String verCode = RandomNumberCode.verCode();
    int result = cacheUtil.cacheForVerificationCode(VERIFYCODE_PREFIX+mobile,verCode,"reg",60,ip);
    if (result == 1) {
        log.info("当前验证码未过期,请稍后重试");
        throw new CustomException("当前验证码未过期,请稍后重试");
    } else if (result == 2) {
        log.info("超过当日验证码次数上线");
        throw new CustomException("超过当日验证码次数上限");
    } else if (result == 3) {
        log.info("超过当日验证码次数上限 {}", ip);
        throw new CustomException(ip + "超过当日验证码次数上限");
    }
    log.info("Sending verify code {} for phone {}", verCode, mobile);
    //校验通过 发送短信 发消息到队列
    Destination destination = new ActiveMQQueue(SMS_QUEUE);
    Map smsParam = new HashMap<>();
    smsParam.put("mobile",mobile);
    smsParam.put("tplId", Constants.MDSMS_VERCODE_TPLID);
    smsParam.put("vercode",verCode);
    String message = JSON.toJSONString(smsParam);
    smsProcessor.sendSmsToQueue(destination,message);
    
} }

RandomNumberCode方法生成四位随机验证码

public class RandomNumberCode {

    public static String verCode(){
        Random random =new Random();
        return StringUtils.substring(String.valueOf(random.nextInt()*-10), 2, 6);
    }
    public static String randomNo(){
        Random random =new Random();
        return String.valueOf(Math.abs(random.nextInt()*-10));
    }
}

将验证码信息存到redis缓存中

springboot整合redis

添加配置文件在application.yml中

redis:
    host: 127.0.0.1
    port: 6379
    auth:
    max-idle: 5
    max-total: 10
    max-wait-millis: 3000

新建一个类来获取变量值:

@Data    //该注释自动帮你完成get/set方法,在pom.xml文件引入org.projectlombok包,加速开发
/*注入*/
@Component
public class Parameters {

    /*****redis config start*******/
    @Value("${redis.host}")
    private String redisHost;
    @Value("${redis.port}")
    private int redisPort;
    @Value("${redis.auth}")
    private String redisAuth;
    @Value("${redis.max-idle}")
    private int redisMaxTotal;
    @Value("${redis.max-total}")
    private int redisMaxIdle;
    @Value("${redis.max-wait-millis}")
    private int redisMaxWaitMillis;
    /*****redis config end*******/
}

创建JedisPoolWrapper类

@Component
@Slf4j
public class JedisPoolWrapper {

    private JedisPool jedisPool = null;

    @Autowired
    private Parameters parameters;


    @PostConstruct
    public void init() throws CustomException {
        try{
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxIdle(parameters.getRedisMaxIdle());
            config.setMaxTotal(parameters.getRedisMaxTotal());
            config.setMaxWaitMillis(parameters.getRedisMaxWaitMillis());
            jedisPool = new JedisPool(config,parameters.getRedisHost(),parameters.getRedisPort(),2000);
        }catch (Exception e){
            log.error("Fail to initialize redis pool",e);
            throw new CustomException("初始化redis失败");
        }

    }

    public JedisPool getJedisPool(){
        return jedisPool;
    }


}

创建一个CommonCacheUtil工具类

@Component
@Slf4j
public class CommonCacheUtil {

    private static final String TOKEN_PREFIX = "token.";

    private static final String USER_PREFIX = "user.";

    @Autowired
    private JedisPoolWrapper jedisPoolWrapper;

    /**
     * 缓存 key value 永久
     * @param key
     * @param value
     */
    public void cache(String key, String value) {
        try {
            JedisPool pool = jedisPoolWrapper.getJedisPool();
            if (pool != null) {
                try (Jedis Jedis = pool.getResource()) {
                    Jedis.select(0);//选择redis第0片区
                    Jedis.set(key, value);
                }
            }
        } catch (Exception e) {
            log.error("Fail to cache value", e);
        }
    }

    /**
     * 获取缓存key
     * @param key
     * @return
     */
    public String getCacheValue(String key) {
        String value = null;
        try {
            JedisPool pool = jedisPoolWrapper.getJedisPool();
            if (pool != null) {
                try (Jedis Jedis = pool.getResource()) {
                    Jedis.select(0);
                    value = Jedis.get(key);
                }
            }
        } catch (Exception e) {
            log.error("Fail to get cached value", e);
        }
        return value;
    }

    /**
     * 设置key value 以及过期时间
     * @param key
     * @param value
     * @param expiry
     * @return
     */
    public long cacheNxExpire(String key, String value, int expiry) {
        long result = 0;
        try {
            JedisPool pool = jedisPoolWrapper.getJedisPool();
            if (pool != null) {
                try (Jedis jedis = pool.getResource()) {
                    jedis.select(0);
                    result = jedis.setnx(key, value);
                    jedis.expire(key, expiry);
                }
            }
        } catch (Exception e) {
            log.error("Fail to cacheNx value", e);
        }

        return result;
    }

    /**
     * 删除缓存key
     * @param key
     */
    public void delKey(String key) {
        JedisPool pool = jedisPoolWrapper.getJedisPool();
        if (pool != null) {

            try (Jedis jedis = pool.getResource()) {
                jedis.select(0);
                try {
                    jedis.del(key);
                } catch (Exception e) {
                    log.error("Fail to remove key from redis", e);
                }
            }catch (Exception e) {
                log.error("Fail to remove key from redis", e);
            }
        }
    }
    /**
     * 缓存手机验证码专用 限制了发送次数
     * @return  1 当前验证码未过期   2 手机号超过当日验证码次数上限  3 ip超过当日验证码次数上线
     */
    public int cacheForVerificationCode(String key, String verCode, String type, int second, String ip) throws Exception {
        JedisPool pool = jedisPoolWrapper.getJedisPool();
        if (pool != null) {
            try (Jedis jedis = pool.getResource()) {
                jedis.select(0);
                try {
                    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;
                        }
                        long succ = jedis.setnx(key, verCode);
                        if (succ == 0) {
                            return 1;
                        }
                        String sendCount = jedis.get(key + "." + type);
                        try {
                            if (sendCount != null && Integer.parseInt(sendCount) >= 10) {
                                jedis.del(key);
                                return 2;
                            }
                        } catch (NumberFormatException e) {
                            log.error("Fail to process send count", e);
                            jedis.del(key);
                            return 2;
                        }
                        try {
                            jedis.expire(key, second);
                            long val = jedis.incr(key + "." + type);
                            if (val == 1) {
                                jedis.expire(key + "." + type, 86400);
                            }

                            jedis.incr(ipKey);
                            if (val == 1) {
                                jedis.expire(ipKey, 86400);
                            }
                        } catch (Exception e) {
                            log.error("Fail to cache data into redis", e);
                        }
                    }
                } catch (Exception e) {
                    log.error("Fail to set vercode to redis", e);
                    throw e;
                }
            } catch (Exception e) {
                log.error("Fail to cache for expiry", e);
                throw new Exception("Fail to cache for expiry");
            }
        }
        return 0;
    }
}

新建一个短信接口

public interface SmsSender {
    void sendSms(String phone,String tplId,String params);
}

实现类,依据官方demo作修改

@Service("verCodeService")
@Slf4j
public class MiaoDiSmsSender implements  SmsSender{

    private static String operation = "/industrySMS/sendSMS"; 
    @Override
    public  void sendSms(String phone,String tplId,String params){
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
            String timestamp = sdf.format(new Date());
            String sig = MD5Util.getMD5(Constants.MDSMS_ACCOUNT_SID +Constants.MDSMS_AUTH_TOKEN +timestamp);
            String url = Constants.MDSMS_REST_URL +operation;
            Map param = new HashMap<>();
            param.put("accountSid",Constants.MDSMS_ACCOUNT_SID);
            param.put("to",phone);
            param.put("templateid",tplId);
            param.put("param",params);
            param.put("timestamp",timestamp);
            param.put("sig",sig);
            param.put("respDataType","json");
            String result = HttpUtil.post(url,param);
            JSONObject jsonObject = JSON.parseObject(result);
            if(!jsonObject.getString("respCode").equals("00000")){
                log.error("fail to send sms to "+phone+":"+params+":"+result);
            }
        } catch (Exception e) {
            log.error("fail to send sms to "+phone+":"+params);
        }
    }

}

整合ActiveMQ,在pom.xml文件中加入包依赖


   org.springframework.boot
   spring-boot-starter-activemq

新建一个消息生产者SmsProcessor类

@Component("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"));
    }

}

Controller代码:

@RequestMapping("/sendVercode")
public ApiResult sendVercode(@RequestBody User user, HttpServletRequest request){
    ApiResult resp = new ApiResult();
    try {
        userService.sendVercode(user.getMobile(),getIpFromRequest(request));

    }catch(CustomException e){
        resp.setCode(Constants.RESP_STATUS_INTERNAL_ERROR);
        resp.setMessage(e.getMessage());
    }
    catch(CustomException e){
        log.error("Fail to login",e);
        resp.setCode(Constants.RESP_STATUS_INTERNAL_ERROR);
        resp.setMessage("内部错误");

    }
    return resp;
}

User类

@Data
public class User {
    private Long id;

    private String nickname;

    private String mobile;

    private String headImg;

    private Byte verifyFlag;

    private Byte enableFlag;

}

统一返回数据ApiResult类

public class ApiResult {

        private int code = Constants.RESP_STATUS_OK;

        private String message;

        private T data;

}

自定义异常类:CustomException

public class CustomException extends Exception {
    public CustomException(String message){
        super(message);
    }
}

常量类:Constants

public class Constants {

    /**自定义状态码 start**/
    public static final int RESP_STATUS_OK = 200;

    public static final int RESP_STATUS_NOAUTH = 401;

    public static final int RESP_STATUS_INTERNAL_ERROR = 500;

    public static final int RESP_STATUS_BADREQUEST = 400;
    /**秒滴SMS start**/
    public static final String MDSMS_ACCOUNT_SID = "填写自己的SID";

    public static final String MDSMS_AUTH_TOKEN = "填写自己TOKEN";

    public static final String MDSMS_REST_URL = "https://api.miaodiyun.com/20150822";

    public static final String MDSMS_VERCODE_TPLID = "模板编号";
    /**秒滴SMS end**/
    public static final String REQUEST_TOKEN_KEY = "user-token";

    public static final String REQUEST_VERSION_KEY = "version"
}

考虑短信接口的安全性

1.IP 根据IP来判定 超过十次 不发送验证码

手机号 同一个手机号 不能超过多少次

通过调用redis工具类中的cacheForVerificationCode方法

2.获取真正的ip+本机ip测试(ngnix负载均衡,请求分发)

protected String getIpFromRequest(HttpServletRequest request) {
    String ip = request.getHeader("x-forwarded-for");
    if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
        ip = request.getHeader("Proxy-Client-IP");
    }
    if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
        ip = request.getHeader("WL-Proxy-Client-IP");
    }
    if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){
        ip = request.getRemoteAddr();
    }
    return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
}

你可能感兴趣的:(springboot对接第三方短信接口整合redis,ActiveMQ)