您的位置:首页 > 数据库 > Redis

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

2018-03-17 17:44 856 查看
1.对接第三方短信接口需要模拟一个http请求
用http-client这个包来操作。
在pom.xml文件中加入http-client的包依赖<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>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<String, String> paramsMap) {
CloseableHttpClient client = HttpClients.createDefault();
String responseText = "";
CloseableHttpResponse response = null;
try {
HttpPost method = new HttpPost(url);
if (paramsMap != null) {
List<NameValuePair> paramList = new ArrayList<NameValuePair>();
for (Map.Entry<String, String> 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<String, String> paramsMap) {
CloseableHttpClient client = HttpClients.createDefault();
String responseText = "";
CloseableHttpResponse response = null;
try {
String getUrl = url+"?";
if (paramsMap != null) {
for (Map.Entry<String, String> 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<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);
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
*/
4000
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<String,String> 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文件中加入包依赖<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>新建一个消息生产者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<T> {

private int code = Constants.RESP_STATUS_OK;

private String message;

private T data;

}
自定义异常类:CustomExceptionpublic class CustomException extends Exception {
public CustomException(String message){
super(message);
}
}
常量类:Constantspublic 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: