首頁 > 軟體

Java實現傳送簡訊驗證碼+redis限制傳送的次數功能

2022-04-18 19:01:16

java實現簡訊驗證碼傳送,由於我們使用第三方平臺進行驗證碼的傳送,所以首先,我們要在一個平臺進行註冊。這樣的平臺有很多,有的平臺在新建賬號的時候會附帶贈幾條免費簡訊。這裡我僅做測試使用(具體哪個平臺見參考三,很簡單,註冊賬號就行,記得新增簡訊簽名)。

另外,在實際專案中,如果有人惡意攻擊,不停的傳送簡訊驗證碼,就會造成很大的損失。故對傳送次數做一定的限制就非常必要,這裡我們限制一個手機號一天可以發多少簡訊和簡訊平臺無關。

這裡採用的是存redis來實現這一個功能。就是每次呼叫傳送驗證碼這個介面都會判斷手機號碼是否在redis中存為key了。如果沒有則建立一個key為手機號碼value是1.因為redis中不支援數位所以將其變為了string型別。如果redis中已經有這個key了則將此key的值取出來加1再存進redis中。

程式碼實現:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lmc</groupId>
    <artifactId>springboot-sendsms</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-sendsms</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
 
</project>

RespBean.java

package com.lmc.bean;
 
public class RespBean {
    private Integer status;
    private String msg;
    private Object obj;
 
    public static RespBean build() {
        return new RespBean();
    }
 
    public static RespBean ok(String msg) {
        return new RespBean(200, msg, null);
    }
 
    public static RespBean ok(String msg, Object obj) {
        return new RespBean(200, msg, obj);
    }
 
    public static RespBean error(String msg) {
        return new RespBean(500, msg, null);
    }
 
    public static RespBean error(String msg, Object obj) {
        return new RespBean(500, msg, obj);
    }
 
    private RespBean() {
    }
 
    private RespBean(Integer status, String msg, Object obj) {
        this.status = status;
        this.msg = msg;
        this.obj = obj;
    }
 
    public Integer getStatus() {
        return status;
    }
 
    public RespBean setStatus(Integer status) {
        this.status = status;
        return this;
    }
 
    public String getMsg() {
        return msg;
    }
 
    public RespBean setMsg(String msg) {
        this.msg = msg;
        return this;
    }
 
    public Object getObj() {
        return obj;
    }
 
    public RespBean setObj(Object obj) {
        this.obj = obj;
        return this;
    }
}

RedisConfig.java

package com.lmc.config;
 
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import java.net.UnknownHostException;
@Configuration
public class RedisConfig {
    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(jackson2JsonRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashKeySerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(
        StringRedisTemplate template = new StringRedisTemplate();
}

SMSController.java

package com.lmc.controller;
 
import com.lmc.bean.RespBean;
import com.lmc.utils.HttpClientUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
 * @description:
 * @Author: lmc
 * @date: 2021/12/26 10:21
 */
@RestController
public class SMSController {
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @RequestMapping("/send")
    public RespBean sendSMS() {
        String Uid = "xxxxxxxx";
        String Key = "xxxxxxxxxxxxxxx";
        String smsMob = "xxxxxxxxx";
        String sendSMSCount = "sendSMSCount:" + smsMob;
        if ("2".equals(stringRedisTemplate.opsForValue().get(sendSMSCount))) {
            return RespBean.error("今天已達到傳送簡訊驗證碼上限,請明天再試");
        }
        //簡訊內容
        String smsText = "歡迎使用xx系統,驗證碼:8888";
        Map maps = new HashMap();
        maps.put("Uid", Uid);
        maps.put("Key", Key);
        maps.put("smsMob", smsMob);
        maps.put("smsText", smsText);
        String result = HttpClientUtils.sendHttpPost("http://utf8.sms.webchinese.cn", maps);
        int i = Integer.parseInt(result);
        if (i > 0) {
            if (stringRedisTemplate.opsForValue().get(sendSMSCount) == null) {
                stringRedisTemplate.opsForValue().set(sendSMSCount, "1", getEndTime(), TimeUnit.MILLISECONDS);
            } else {
                String value = stringRedisTemplate.opsForValue().get(sendSMSCount);
                int times = Integer.parseInt(value) + 1;
                String timesStr = String.valueOf(times);
                stringRedisTemplate.opsForValue().set(sendSMSCount, timesStr, getEndTime(), TimeUnit.MILLISECONDS);
            }
            return RespBean.ok("傳送成功");
        return RespBean.ok("傳送失敗");
    }
    
    //獲取當前時間到今天結束時間所剩餘的毫秒數:
    public static long getEndTime() {
        //獲取當前時間的毫秒數
        long time = new java.util.Date().getTime();
        //獲取到今天結束的毫秒數
        Calendar todayEnd = Calendar.getInstance();
        todayEnd.set(Calendar.HOUR_OF_DAY, 23); // Calendar.HOUR 12小時制。HOUR_OF_DAY 24小時制
        todayEnd.set(Calendar.MINUTE, 59);
        todayEnd.set(Calendar.SECOND, 59);
        todayEnd.set(Calendar.MILLISECOND, 999);
        long endTime = todayEnd.getTimeInMillis();
        //這裡endTime-time獲取的是到23:59:59:999的毫秒數。再加1才是到24點整的毫秒數
        return endTime-time+1;
}

HttpClientUtils.java(HttpClient工具類,可以複用)

package com.lmc.utils;
 
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
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.client.methods.HttpRequestBase;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.util.PublicSuffixMatcher;
import org.apache.http.conn.util.PublicSuffixMatcherLoader;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HttpClientUtils {
	private static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);
	// 連結相關引數
	private static int socketTimeout = 15000;
	private static int connectTimeout = 15000;
	private static int connectionRequestTimeout = 15000;
	private static RequestConfig requestConfig = null;
	// 連線池相關引數
	private static int connMgrMaxTotal = 100;
	private static int connMgrMaxPerRoute = 50;
	private static PoolingHttpClientConnectionManager connMgr = null;
	
	static {
		requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).setConnectionRequestTimeout(connectionRequestTimeout).build();
		connMgr = new PoolingHttpClientConnectionManager();
		connMgr.setDefaultMaxPerRoute(connMgrMaxPerRoute);
		connMgr.setMaxTotal(connMgrMaxTotal);
	}
	private static String doHttp(HttpRequestBase httpRequestBase) {
		CloseableHttpClient httpClient = null;
		CloseableHttpResponse response = null;
		String responseContent = null;
		
		try {
			// 建立預設的httpClient範例.
			String scheme = httpRequestBase.getURI().getScheme();
			if (scheme.equalsIgnoreCase("https")) {
				PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(new URL(httpRequestBase.getURI().toString()));
				DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
				httpClient = HttpClients.custom().setSSLHostnameVerifier(hostnameVerifier).setConnectionManager(connMgr).build();
				//httpClient = HttpClients.custom().setSSLHostnameVerifier(hostnameVerifier).build();
			} else if (scheme.equalsIgnoreCase("http")) {
				httpClient = HttpClients.custom().setConnectionManager(connMgr).build();
				//httpClient = HttpClients.createDefault();
			} else {
				throw new IllegalArgumentException("url的scheme錯誤,必須是http或者https! ");
			}
			httpRequestBase.setConfig(requestConfig);
			// 執行請求
			response = httpClient.execute(httpRequestBase);
			// 如果這裡有必要獲取的是其他資料都可以在這裡進行邏輯處理
			responseContent = EntityUtils.toString(response.getEntity(), "UTF-8");
			return responseContent;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				// 關閉連線,釋放資源
				if (response != null) {
					// EntityUtils.consume(response.getEntity());
					response.close();
				}
				// 這裡不能關閉httpClient,這個會關連結池
				//if (httpClient != null) {
				//  httpClient.close();
				//}
				
			} catch (IOException e) {
				e.printStackTrace();
		}
		return responseContent;
	/**
	 *  sendHttpGet(url)
	 * @param url
	 * @return
	 */
	public static String sendHttpGet(String url) {
		return doHttp(new HttpGet(url));
	 * sendHttpGet()
	 * @param param key1=value1&key2=value2&key3=value3
	public static String sendHttpGet(String url, String param) {
		// 建立httpGet
		HttpGet httpGet = new HttpGet(url + '?' + param);
		return doHttp(httpGet);
	 * sendHttpPost()
	public static String sendHttpPost(String url, String param) {
		// 建立httpPost
		HttpPost httpPost = new HttpPost(url);
			StringEntity stringEntity = new StringEntity(param, "UTF-8");
			stringEntity.setContentType("application/x-www-form-urlencoded");
			httpPost.setEntity(stringEntity);
		return doHttp(httpPost);
	 * sendHttpGet
	 * @param param 是個map<String, String>
	public static String sendHttpGet(String url, Map<String, String> param) {
		String paramStr = "";
		for (String key : param.keySet()) {
			String tmp = "";
			tmp = "&" + key + "=" + param.get(key);
			paramStr += tmp;
		paramStr = paramStr.substring(1);
		HttpGet httpGet = new HttpGet(url + '?' + paramStr);
        return doHttp(httpGet);
	 * sendHttpPost
	 * @param param 是個map<String,String>
	public static String sendHttpPost(String url, Map<String, String> param) {
		// 建立引數佇列 
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        for (String key : param.keySet()) {
            nameValuePairs.add(new BasicNameValuePair(key, param.get(key)));   
        }
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));    
        } catch (Exception e) {
            e.printStackTrace();
        
        return doHttp(httpPost);
	public static String sendHttpPostJson(String url, String json) {
			// StringEntity stringEntity = new StringEntity(param, "UTF-8");
			// stringEntity.setContentType("application/json");
			// stringEntity.setContentEncoding("UTF-8");
			StringEntity stringEntity = new StringEntity(json, ContentType.create("application/json", "UTF-8"));
						
	public static void main(String[] args) {
		String url = "http://api.crpay.com/payapi/gateway";
		String param = "merchant_no=TOF00001&method=unified.trade.pay&version=1.0";
		Map map = new HashMap<String, String>();
		map.put("merchant_no", "TOF00001");
		map.put("method", "unified.trade.pay");
		map.put("version", "1.0");
		// 這個工具是走的連結池,但是在關閉httpClient會關閉連線池的地方已經登出
		//System.out.println(HttpClientUtils.sendHttpPost(url, map));
		//System.out.println(HttpClientUtils.sendHttpPost(url, param));
		//System.out.println(HttpClientUtils.sendHttpGet(url, map));
		System.out.println(HttpClientUtils.sendHttpGet("https://www.baidu.com"));
		System.out.println(HttpClientUtils.sendHttpGet("http://www.baidu.com/s?wd=aaa"));
		Map map2 = new HashMap<String, String>();
		map2.put("wd", "aaa");
		System.out.println(HttpClientUtils.sendHttpGet("http://www.baidu.com/s",map2));
		// doHttp是靜態私有方法,不能使用多次,會報Connection pool shut down 
		System.out.println(HttpClientUtils.doHttp(new HttpGet("http://www.baidu.com/s?wd=aaa")));
		System.out.println(HttpClientUtils.doHttp(new HttpGet("https://www.baidu.com/")));
		System.out.println(HttpClientUtils.sendHttpGet("https://www.cnblogs.com/hugo-zhangzhen/p/6858013.html"));
		System.out.println(HttpClientUtils.sendHttpGet("https://www.cnblogs.com/hugo-zhangzhen/p/6739658.html"));
		System.out.println(HttpClientUtils.sendHttpGet("https://www.cnblogs.com/hugo-zhangzhen/p/6737810.html"));
		System.out.println(HttpClientUtils.sendHttpGet("http://blog.csdn.net/xiechengfa/article/details/42016153"));
}

application.properties

# 設定redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=123456

專案結果如下:

 結果展示:

使用postman呼叫介面,超過2次後,顯示如下。

在具體專案中的流程一般如下:

①構造手機驗證碼,需要生成一個6位的亂數字串;

②找簡訊平臺獲取使用介面向簡訊平臺傳送手機號和驗證碼,然後簡訊平臺再把驗證碼傳送到制定手機號上;

③將手機號驗證碼、操作時間存入Session中,作為後面驗證使用;

④接收使用者填寫的驗證碼、手機號及其他註冊資料;

⑤對比提交的驗證碼與Session中的驗證碼是否一致,同時判斷提交動作是否在有效期內;

⑥驗證碼正確且在有效期內,請求通過,處理相應的業務。

參考:

接收簡訊驗證碼條數限制(java傳送簡訊驗證碼限制) - 簡書

Java如何實現簡訊驗證碼功能? - 知乎

Java 實現手機傳送簡訊驗證碼 - 胖頭陀春天 - 部落格園

到此這篇關於Java實現傳送簡訊驗證碼+redis限制傳送的次數的文章就介紹到這了,更多相關java簡訊驗證碼限制傳送次數內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com