「PHP开发APP接口实战012」发送短信验证码之生成并缓存验证
2018-03-07 本文已影响35人
一念觀心
配置参数
- Memcache 配置, 在
/app/config/config.ini
文件添加以下代码:
[memcache]
host = 127.0.0.1
port = 11211
prefix = api
在 Visual NMP 中,默认已经安装了 Memcache, 可直接使用。如服务未开启,直接打开即可。 默认连接端口:
11211
。缓存键名前缀:prefix = api
,方便区分项目,可随意设置, 一般设置为项目名。
「PHP开发APP接口实战001」开发环境搭建
- 短信配置, 在
/app/config/config.php
文件添加以下代码:
'sms' => [
'times' => 3, // 同一手机一小时内发送短信次数, 0 为不限制
'interval' => 60, // 同一手机两次发送间隔时间(单位:秒), 0 为不限制
'valid_time' => 300, // 短信验证码有效时间(单位:秒), 0 为久有效
],
创建 Memcache 操作类 XMemcache
在 /app/library
目录下创建文件 XMemcache.php
, 添加以下代码:
<?php
/**
* 缓存
*/
class XMemcache
{
public static $instance;
private $memcache = null; // Memcache 对象
private $config = null; // 配置参数
private $tag = null; // 标识
private function __construct($tag = null)
{
// 初始化 Memcache 对象
$this->memcache = new Memcache();
// 加载配置参数
$this->config = Config::instance()->get('memcache', 'ini');
// 连接Memcache服务器
$this->memcache->addServer($this->config['host'], $this->config['port']);
$this->tag = $tag;
}
/**
* @param null $tag 缓存标识
* @return XMemcache
*/
public static function instance($tag = null)
{
if (!self::$instance) self::$instance = new self($tag);
return self::$instance;
}
/**
* 添加缓存,若不存在则追加,若不存在则新增
* @param $key 键名
* @param $value 缓存内容
* @param int $timeout 0永久有效,604800 7天,最大不能超过30天
* @param int $iszip
* @return mixed
*/
public function append($key, $value, $timeout = 604800, $iszip = 0)
{
if ($values = $this->get($key)) {
$values[] = $value;
} else {
$values = [$value];
}
$this->set($key, $values, $timeout, $iszip);
}
/**
* 设置缓存
* @param $key 键名
* @param $value 缓存内容
* @param int $timeout 0永久有效,604800 7天,最大不能超过30天
* @param int $iszip
* @return mixed
*/
public function set($key, $value, $timeout = 604800, $iszip = 0)
{
$value = serialize($value);
return $this->memcache->set($this->formatKey($key), $value, $iszip, $timeout);
}
/**
* 根据KEY获得缓存内容
* @param $key
* @return mixed
*/
public function get($key)
{
$value = $this->memcache->get($this->formatKey($key));
return unserialize($value);
}
/**
* 删除指定缓存
* @param $key
* @return mixed
*/
public function delete($key)
{
return $this->memcache->delete($this->formatKey($key));
}
/**
* 清空缓存
* @return mixed
*/
public function flush()
{
return $this->memcache->flush();
}
/**
* 重写缓存键名,格式: [prefix]:[tag]:[key]
* @param $key
* @return string
*/
private function formatKey($key)
{
return $this->config['prefix'] . ':' . $this->tag . ':' . $key;
}
}
这里重写了一些
Memcache
常用的操作函数。 如:设置缓存set()
, 追加缓存append()
, 获取缓存get()
, 删除缓存delete()
, 清空缓存flush()
值得注意的是,我们还对缓存键名进行了重写,方便区分项目和模块。
生成短信验证码
- 在
/app/library
目录下创建文件SMS.php
, 添加以下代码:
<?php
/**
* 短信验证码
*/
class SMS
{
public static $instance;
// 配置参数
private $config = null;
private function __construct()
{
// 加载短信配置参数
$this->config = Config::instance()->get('sms');
}
public static function instance()
{
if (!self::$instance) self::$instance = new self();
return self::$instance;
}
}
这里实现了实例化时,自己加载配置参数。
- 增加验证码改送函数
send()
, 用于外部调用。如:
/**
* 发送短信验证码
* @param $mobile
* @return array
* @throws Exception
*/
public function send($mobile)
{
}
此函数里面分四步走:
- 验证指定手机号,当前是否可以发送验证码
- 生成四位数字验证码,并配置上生成时间
- 调用
XMemcache
缓存验证码- 调用第三方接口,发送验证码,并返回发送状态。(市场上有许多发送第三方平台,都有)
- 这里调换一下顺序,先讲解生成和缓存验证码。
首先,添加函数generateCode()
, 随机生成4位数字验证码
/**
* 随机生成4位数字验证码
* @return int
*/
private function generateCode()
{
return rand(1000, 9999);
}
然后,在 send()
函数中添加代码:
$item = [
'code' => $this->generateCode(), // 生成短信验证码
'time' => time(), // 生成时间
'verified' => 0, // 验证状态: 0 未验证, 1 已验证
];
这里除了生成验证码,同时初始化生成时间
time
, 验证状态verified
,用于验证发送时间和检查验证码是否已验证。
- 缓存验证码,在
send()
函数中添加代码:
// 缓存短信验证码
XMemcache::instance('sms')->append($mobile, $item, 3600);
这里完成了几个工作:
- 将
$item
存于以指定手机号为键名的缓存下- 同一手机号多次发送,都存在同一键名下,用于统计1小时内验证码发送次数。
- 缓存有效时间设置为 1 小时
- 现在我们再回来讲解验证是否允许向指定手机号发送验证码。
验证规则:
- 同一手机两次发送间隔时间1分钟(可配置间隔时间)
- 同一手机1小时内最多只能发送3次验证码(可配置发送次数)
首先,添加函数 getCacheCodes()
和 validateSend()
, 如:
/**
* 获取1小时内发送的验证码
* @param $mobile
* @return null
*/
private function getCacheCodes($mobile)
{
$codes = XMemcache::instance('sms')->get($mobile);
if (!$codes)
return [];
foreach ($codes as $index => $item) {
// 过滤发送超过1小时的验证码
if (time() - $item['time'] > 3600) {
unset($codes[$index]);
}
}
// 重置数组索引
$codes = array_values($codes);
// 更新缓存
XMemcache::instance('sms')->set($mobile, $codes);
return $codes;
}
/**
* 验证是不否允许发送
* @param $mobile
* @throws Exception
*/
private function validateSend($mobile)
{
$codes = $this->getCacheCodes($mobile);
if ($this->config['times'] > 0 && count($codes) >= $this->config['times']) {
throw new Exception('一小时内最多只能发送' . $this->config['times'] . '次短信验证码');
}
$lastCode = end($codes);
if ($this->config['interval'] > 0 && time() - $lastCode['time'] <= $this->config['interval']) {
throw new Exception('发送频率太快');
}
}
- 函数
getCacheCodes()
获取指定手机1小时内发送的验证码。- 函数
validateSend()
实现:
验证一小时内向同一手机发送短信验证码次数是否超过了配置次数;
验证上次发送验证码是否已经超过配置时间;
然后在 send()
函数中,所有代码之前插入代码 $this->validateSend($mobile);
。
send()
函数完整代码:
/**
* 发送短信验证码
* @param $mobile
* @return array
* @throws Exception
*/
public function send($mobile)
{
// 验证短信发送次数
$this->validateSend($mobile);
$item = [
'code' => $this->generateCode(), // 生成短信验证码
'time' => time(), // 生成时间
'verified' => 0, // 验证状态: 0 未验证, 1 已验证
];
// 缓存短信验证码
XMemcache::instance('sms')->append($mobile, $item, 3600);
// 发送短信验证码
/* ... 调用第三方接口 ... */
return $item;
}
- 再在控制器
SmsController
类的sendAction()
函数中加入以下代码:
// 发送验证码
$result = SMS::instance()->send($this->getPost('user_mobile'));
if ($result) {
// Output::instance($this->response)->success(’发送成功‘);
Output::instance($this->response)->success((object)$result);
} else {
Output::instance($this->response)->fail('发送失败');
}
这里没有真正实现调用第三方接口,而是直接返回了发送的验证码,以供测试使用。正式代码,只返回发送状态。
SmsController.php
完整代码:
<?php
class SmsController extends BaseController
{
/**
* 发送短验证码
*/
public function sendAction()
{
// 验证请求方法是否是POST
$this->isPost();
// 验证请求参数
XValidationSms::send($this->getPost());
// 发送验证码
$result = SMS::instance()->send($this->getPost('user_mobile'));
if ($result) {
// Output::instance($this->response)->success(’发送成功‘);
Output::instance($this->response)->success((object)$result);
} else {
Output::instance($this->response)->fail('发送失败');
}
}
}
接口调试示例
- 请求地址:http://127.0.0.1:20081/sms/send
- 请求方式:POST
- 请求参数:user_mobile=18088888888
- 返回数据:
{
"status": "1",
"value": "发送成功"
}
开发调试时返回数据 :
{
"status": "1",
"item": {
"code": "1139",
"time": "1520663958",
"verified": "0"
}
}
接口调试示例图
示例代码下载
链接:https://pan.baidu.com/s/1gvyi8eX3JdlEUzzndpLVoQ 密码:839z