短信通道防盗刷,短信发送策略

一、短信通道防盗刷方案

一、使用安全图形验证码,增加识别难度,防止通过自动化工具进行攻击请求;
规则:使用手滑动形式的验证码。
二、限制每个手机号的发送次数;
规则:每个手机号每天最多只能发10条短信;   
三、单Ip的请求次数限制,防止攻击者对服务器进行大量无效请求(在图形验证码未破解的情况下,自动化工具行程错误请求),增加服务器负担;
规则:限制单IP每天请求次数不能超过10次。
四、单用户动态短信请求间隔时长限制,防止对单个用户形成手工攻击,防止图形验证码失效后对用户形成大量攻击;
规则:单用户请求短信时间间隔为“60秒”;    
五、增加IP黑名单库,在黑名单库的Ip永久不能获取验证码;管理员可以手动添加IP黑名单,可以手动删除黑名单;
黑名单规则:
1)同一号码在同一天内发送超过10条短信; 
2)同一IP在1分钟内出现3次以上; 
3)同一IP在30分钟内超过5次以上;
4)同一IP在60分钟内出现10次以上 ; 
5)同一IP在48*60内出现20以上 ;

以上为方案参考

二、参数配置短信策略

在这里插入图片描述

三、代码实现

技术方案:
1.将参数存入redis缓存,每次从缓存中获取参数;
2.短信间隔60秒用redis缓存,有效期设置为60秒
3.请求次数存入redis,每次请求加1,到达上限则限制发送短信

====短信策略Service类

package com.shijie.box.service;

import com.shijie.box.db.util.StringUtil;
import com.shijie.box.util.IpUtil;
import com.shijie.box.vo.ApiResult;
import com.shijie.box.vo.SmsFangweiSendRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

/**
 * 短信发送策略  wy
 * 2020/7/29
 */
@Service
public class SmsPolicyService {
	private static final Logger logger = LoggerFactory.getLogger(SmsPolicyService.class);

	private static final String MOBILE_PROFIX = "SMS_MOBILE_";//短信手机号缓存类型
	private static final String IP_PROFIX = "SMS_IP_";//短信手机号缓存类型
	private static final String SMSTIME = "SMSTIME_";//短信手机号缓存有效时长

	private static final String SMS_IP_BLACK_LIST = "SMS_IP_BLACK_LIST";//【短信策略】IP黑名单,以";"间隔
	private static final String SMS_IP_WHITE_LIST = "SMS_IP_WHITE_LIST";//【短信策略】IP白名单,当前IP不做任何策略限制,以“;”间隔
	private static final String SMS_TIME_SPACING = "SMS_TIME_SPACING";//【短信策略】单用户(IP)请求短信时间间隔为“60秒”
	private static final String SMS_COUNT_PER_IP = "SMS_COUNT_PER_IP";//【短信策略】限制单IP每天请求次数不能超过10次
	private static final String SMS_COUNT_PER_MOBILE = "SMS_COUNT_PER_MOBILE";//【短信策略】每个手机号每天最多只能发10条短信


	@Autowired
	com.shijie.box.db.util.RedisUtil redisUtil;

	@Resource
    com.shijie.box.db.dao.SxbSysParameterMapper SxbSysParameterMapper;

	/**
	 * 短信发送策略
	 * @param request
	 * @return
	 */
	public ApiResult policy(SmsFangweiSendRequest parameter, HttpServletRequest request) {
		ApiResult rest=new ApiResult("0000","短信策略通过");
		String ipAddr = IpUtil.getIpAddr(request);
		String whiteList = redisUtil.get(SMS_IP_WHITE_LIST);//ip白名单
		//如果缓存中未存短信策略,则开始缓存
		if(StringUtil.isEmpty(whiteList)){
			refreshSMSPolicy();
			whiteList = redisUtil.get(SMS_IP_WHITE_LIST);//ip白名单
		}

		//=======如果是IP白名单,直接通过策略===============//
		if(whiteList.indexOf(ipAddr)>=0){
			logger.info("[短信策略]请求IP:"+ipAddr+",IP白名单放行");
			return rest;
		}

		//===========如果ip黑名单,则直接返回,不通过策略===============//
		String blackList = redisUtil.get(SMS_IP_BLACK_LIST);//ip黑名单
		if(blackList.indexOf(ipAddr)>=0){
			logger.info("[短信策略]请求IP:"+ipAddr+",IP黑名单,做拦截");
			return new ApiResult("0001","未发送,当前ip:"+ipAddr+"已被限制发送短信,请联系管理员");
		}

		//================【短信策略】限制单IP每天请求次数不能超过10次================//
		int SMSIpCount = Integer.parseInt(redisUtil.get(SMS_COUNT_PER_IP));
		String ipCountKey = IP_PROFIX+ipAddr;
		int ipCount = StringUtil.isEmpty(redisUtil.get(ipCountKey))?0: Integer.parseInt(redisUtil.get(ipCountKey));
		if(ipCount>=SMSIpCount){
			logger.info("[短信策略]请求IP:"+ipAddr+",当前IP,今日发送短信次数过多,已超过"+SMSIpCount+"次");
			return new ApiResult("0002","当前IP今日发送短信次数过多,已超过"+SMSIpCount+"次");
		}

		//================【短信策略】每个手机号每天最多只能发10条短信================//
		int SMSMobileCount = Integer.parseInt(redisUtil.get(SMS_COUNT_PER_MOBILE));
		String mobileCountKey = MOBILE_PROFIX+parameter.getMobile();
		int mobileCount = StringUtil.isEmpty(redisUtil.get(mobileCountKey))?0: Integer.parseInt(redisUtil.get(mobileCountKey));
		if(mobileCount>=SMSMobileCount){
			logger.info("[短信策略]请求IP:"+ipAddr+",当前手机号"+parameter.getMobile()+",今日发送短信次数过多,已超过"+SMSMobileCount+"次");
			return new ApiResult("0002","当前手机号今日发送短信次数过多,已超过"+SMSMobileCount+"次");
		}

		//================【短信策略】单用户(IP)请求短信时间间隔为“60秒”============//
		String rediskeyIp = SMSTIME+ipAddr;
		Long smsTimeSpacing = Long.parseLong(redisUtil.get(SMS_TIME_SPACING));//【短信策略】单用户(IP)请求短信时间间隔为“60秒”
		if(!StringUtil.isEmpty(redisUtil.get(rediskeyIp))){
			logger.info("[短信策略]请求IP:"+ipAddr+",请勿频繁请求,再次获取请间隔");
			return new ApiResult("0002","请勿频繁请求,再次获取请间隔"+smsTimeSpacing+"秒");
		}


		//================【短信策略】限制单IP每天请求次数不能超过10次===当前为修改缓存中的次数=============//
		if(ipCount==0){
			redisUtil.setEx(ipCountKey,++ipCount+"",86400, TimeUnit.SECONDS);//1天
		}else{
			redisUtil.setEx(ipCountKey,++ipCount+"",redisUtil.getExpire(ipCountKey), TimeUnit.SECONDS);
		}

		//================【短信策略】每个手机号每天最多只能发10条短信====当前为修改缓存中的次数============//
		if(mobileCount==0){
			redisUtil.setEx(mobileCountKey,++mobileCount+"",86400, TimeUnit.SECONDS);//1天
		}else{
			redisUtil.setEx(mobileCountKey,++mobileCount+"",redisUtil.getExpire(mobileCountKey), TimeUnit.SECONDS);
		}

		//================【短信策略】单用户(IP)请求短信时间间隔为“60秒”====当前为修改缓存中的秒数========//
		//设置当前ip再次发送短信时间间隔为60秒
		redisUtil.setEx(rediskeyIp,ipAddr,smsTimeSpacing, TimeUnit.SECONDS);

		return rest;
	}

	/**
	 * 刷新短信发送策略缓存
	 */
	public void refreshSMSPolicy(){
			//设置缓存有效期为2天,两天后发送短信时候,会再次加载此缓存
			redisUtil.setEx(SMS_IP_BLACK_LIST,SxbSysParameterMapper.selectByPrimaryKey(SMS_IP_BLACK_LIST).getpValue().trim(),2, TimeUnit.DAYS);
			redisUtil.setEx(SMS_IP_WHITE_LIST,SxbSysParameterMapper.selectByPrimaryKey(SMS_IP_WHITE_LIST).getpValue().trim(),2, TimeUnit.DAYS);
			redisUtil.setEx(SMS_TIME_SPACING,SxbSysParameterMapper.selectByPrimaryKey(SMS_TIME_SPACING).getpValue().trim(),2, TimeUnit.DAYS);
			redisUtil.setEx(SMS_COUNT_PER_IP,SxbSysParameterMapper.selectByPrimaryKey(SMS_COUNT_PER_IP).getpValue().trim(),2, TimeUnit.DAYS);
			redisUtil.setEx(SMS_COUNT_PER_MOBILE,SxbSysParameterMapper.selectByPrimaryKey(SMS_COUNT_PER_MOBILE).getpValue().trim(),2, TimeUnit.DAYS);
	}
}

====短信发送Controller

@Controller
@RequestMapping("/sms/fangwei")
@Api("短信相关接口")
public class SmsFangWeiApi {
	@Autowired
	private HttpServletRequest request;
	@Autowired
	private  SmsFangweiService service;

	@PostMapping("/send")
	@ApiOperation("发送短信api")
	@ResponseBody
	public ApiResult<String> send(@RequestBody  SmsFangweiSendRequest  SmsFangweiSendRequest) {

		ApiResult<String>	 rest= service.send(SmsFangweiSendRequest,request) ;

		return rest;
	}
}

=====发送短信service

@Service
public class SmsFangweiService {

    private static final Logger logger = LoggerFactory.getLogger(SmsFangweiService.class);

    @Autowired
    private SmsPolicyService SmsPolicyService;

    public  String send(SmsFangweiSendRequest request, HttpServletRequest httpServletRequest) {
        logger.info("进入短信发送策略{}","短信策略参数配置");
        //=========短信策略,0000代码策略通过================//
        ApiResult policy = SmsPolicyService.policy(request, httpServletRequest);
        //如果通过策略则走发短信流程
        if("0000".equals(policy.getError_code())) {
            //发送短信方法
            sendSMS();
        }else{
            logger.info("【未通过短信策略】,"+policy.getMessage());
        }

        if(!"0000".equals(policy.getError_code())){
            return "error";
        }else {
            return "success";
        }
    }
}

代码中用到的两个IPUtil和RedisUtil下载链接:
链接:https://pan.baidu.com/s/1KE0_wXvbqaK2hQR0Ikc_gg
提取码:4ujn

以上如果获取手机流量的IP不准确,可以参考文章:
java获取手机IP地址不准确解决
https://blog.csdn.net/wangyue23com/article/details/107764209

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

wangyue23com

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

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

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

打赏作者

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

抵扣说明:

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

余额充值