场景
在注册,修改密码,找回密码等场景里,我们都会遇到发送手机短信进行验证码验证,我们都知道,手机的这个短信接口是需要购买了,为了防刷,我们就会对短信验证码发送次数的限制,我们应该如何防止呢?
很多人都会这样做:对用户获取短信验证码的手机号、ip、和浏览器(使用唯一标识)进行限制。
本文介绍的方法是对用户每天只能通过同一浏览器或同一ip地址获取验证码10次或者同一手机号只能获取3次短信验证码,三种限制为“或”关系,一条超限就不发验证码。方法是通过在服务器端将用户的手机号、ip、ur_r标识记录并写入文件,再通过读取文件记录判断用户请求发送验证码的次数来做限制。
方法如下:
这里是获取短信验证码页面:
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- </head>
- <body>
- <!-- 隐藏表单uv_r标识,用于对获取验证码的浏览器进行限制,唯一标识存储于浏览器cookie中。在用户进行获取短信验证码操作时将标识传入后台代码(可以通过js传入后台,此处未提供js代码) -->
- <form action="./chuli.php" method="post">
- <input type="hidden" name="uv_r" value="" id="uv_r">
- <input type="text" name="tel" value="" > 电话
- <input type="submit" value="提交">
- </form>
- </body>
- <script>
- /**
- 使用js获取cookie中ur_r唯一标识,如果不存在,生成唯一标识,js写入cookie,并将唯一标识赋给隐藏表单。
- */
- //唯一标识存入cookie
- var _uuid = getUUID();
- if (getCookie("_UUID_UV") != null && getCookie("_UUID_UV") != undefined) {
- _uuid = getCookie("_UUID_UV");
- } else {
- setCookie("_UUID_UV", _uuid);
- }
- document.getElementById("uv_r").value = _uuid;//赋给hidden表单
- //生成唯一标识
- function getUUID() {
- var uuid = new Date().getTime();
- var randomNum = parseInt(Math.random() * 1000);
- return uuid + randomNum.toString();
- }
- //写cookie
- function setCookie(name, value) {
- var Days = 365;//这里设置cookie存在时间为一年
- var exp = new Date();
- exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
- document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString();
- }
- //获取cookie
- function getCookie(name) {
- var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
- console.log(document.cookie.match(reg));
- if (arr = document.cookie.match(reg))
- return unescape(arr[2]);
- else
- return null;
- }
- console.log(_uuid);
- </script>
- </html>
后端PHP短信限制次数等的封装类(这个类还有点问题看你是怎么调用了 稍作修改,太懒,不想弄)
- <?php
- Class regMod
- {
- //定义全局变量,用于设置记录文件的路径
- Protected $Root = null;
- Public function __construct()
- {
- $this->Root = "./";//自己定义的文件存放位置
- }
- //获取短信验证码操作(Ajax方法为好)
- Public function get_authentication_code($ip = '',$tel = '', $uv_r = '')
- {
- if ($uv_r && $tel) {
- if (empty($uv_r)) {
- $uv_r = 0;
- }
- }
- //判断数据是否超过了限制
- $uvr_num = $this->checkUvr($uv_r);
- $tel_num = $this->checkTel($tel);
- $ip_num = $this->checkIp($ip);
- if ($uvr_num < 10 && $tel_num < 4 && $ip_num < 10) {
- echo "发送验证码";//符合发送条件,发送验证码的操作
- return 1; //验证码发送成功,返回1
- } else {
- //当不发送验证码时,将数据存入文件,用于方便查询
- $data = $tel . "|" . $ip . "|" . $uv_r . "|";
- if ($uv_r > 0 && $uvr_num >= 10) {
- $data = $data . "A@";
- }
- if ($tel_num >= 4) {
- $data = $data . "B@";
- }
- if ($ip_num >= 10) {
- $data = $data . "C@";
- }
- $this->wirteFile("", $data);
- return 2; //发送次数过多
- }
- }
- //以下方法为私有方法
- //检测ur_r在文件中出现的次数
- Private function checkUvr($data)
- {
- $fileName = "Uv_" . date("Ymd", time()) . ".dat";
- $filePath = ($this->Root) . $fileName;//组装要写入的文件的路径
- $c_sum = 0;
- if (file_exists($filePath)) {//文件存在获取次数并将此次请求的数据写入
- $arr = file_get_contents($filePath);
- $row = explode("|", $arr);
- $countArr = array_count_values($row);
- $c_sum = $countArr[$data];
- if ($c_sum < 10) {
- $this->wirteFile($filePath, $data . "|");
- }
- return $c_sum;
- } else {//文件不存在创建文件并写入本次数据,返回次数0
- $this->wirteFile($filePath, $data . "|");
- return $c_sum;
- }
- }
- //检测Tel在文件中出现的次数
- Private function checkTel(string $data)
- {
- $fileName = "Tel_" . date("Ymd", time()) . ".dat";
- $filePath = ($this->Root) . $fileName;
- $c_sum = 0;
- if (file_exists($filePath)) {
- $arr = file_get_contents($filePath);
- $row = explode("|", $arr);
- $countArr = array_count_values($row);
- $c_sum = $countArr[$data];
- if ($c_sum < 4) {
- $this->wirteFile($filePath, $data . "|");
- }
- return $c_sum;
- } else {
- $this->wirteFile($filePath, $data . "|");
- return $c_sum;
- }
- }
- //检测IP在文件中存在的次数
- Private function checkIp($data)
- {
- $fileName = "Ip_" . date("Ymd", time()) . ".dat";
- $filePath = ($this->Root) . $fileName;
- $c_sum = 0;
- if (file_exists($filePath)) {
- $arr = file_get_contents($filePath);
- $row = explode("|", $arr);
- $countArr = array_count_values($row);
- $c_sum = $countArr[$data];
- if ($c_sum < 10) {
- $this->wirteFile($filePath, $data . "|");
- }
- return $c_sum;
- } else {
- $this->wirteFile($filePath, $data . "|");
- return $c_sum;
- }
- }
- /**
- * 将数据写入本地文件
- * @param $filePath 要写入文件的路径
- * @param $data 写入的数据
- */
- Private function wirteFile($filePath, $data)
- {
- try {
- if (!is_dir($this->Root)) {//判断文件所在目录是否存在,不存在就创建
- mkdir($this->Root, 0777, true);
- }
- if ($filePath == "") {//此处是不发送验证码时,记录日志创建的文件
- $filePath = ($this->Root) . "N" . date("Ymd", time()) . ".dat";
- }
- //写入文件操作
- $fp = fopen($filePath, "a+");//得到指针
- fwrite($fp, $data);//写
- fclose($fp);//关闭
- } catch (Exception $e) {
- print $e->getMessage();
- }
- }
- }
- ?>
调用方法(上面说了看你怎么调用 你用ajax可以改下,类里面的方法都是测试过实现没问题)
- <?php
- /**
- * Created by PhpStorm.
- * User: Administrator
- * Date: 2019/6/13 0013
- * Time: 10:23
- */
- include_once './index.php';
- $y = new regMod();
- $tel = 13980718991;
- $uv_r = $_POST['uv_r'];
- $ip = '117.45.123.58';
- $a = $y->get_authentication_code($ip, $tel ,$uv_r);
- var_dump($a);
上一篇:
SSO单点登录-通俗易懂系统讲解
下一篇:
php的无限极分类
友情提示:垃圾评论一律封号...