数据库名
作前缀表名
作前缀缩写
(容易看懂的前提下)用户登陆
举例javascript
# 判断数据库中是否有此用户
post
www.test.com/apiapi.test.com
参数 | 必选 | 类型 | 说明 |
---|---|---|---|
time | true | int | 时间戳 (用于肯定接口的访问时间) |
token | true | string | 肯定访问者身份 (MD5(USER_MD5(time)_USER) ) |
username | true | string | 只接受邮箱 |
password | true | string | 用户密码 |
{ "ret": 200, // 返回结果状态。200:接口正常请求并返回/40*:服务端的数据有误/500:服务器运行错误 "data": { "user_id": "27", // 用户id "user_tag": "1" // 用户身份 }, "msg": "" // 401:用户名不存在!/402:手机号不存在!/403:密码不正确! }
· api.test.com
===> www.test.com/index.php/api
php
httpd-vhosts.conf
添加 ServerAlias api.test.com
host
文件 127.0.0.1 api.test.com
User.php
文件,路径 G:tp5/application/api/controller/User.php
<?php namespace app\api\controller; class User{ public function index($id){ echo '<br/>'; echo $id; } }
config.php
(开启路由)// 是否开启路由
'url_route_on' => true,
// 域名部署
'url_domain_deploy' => true,
route.php
<?php use think\Route; // api.test.com ===> www.test.com/index.php/api Route::domain('api','api');
解决方案: 获取 timestamp (时间戳), 设置接口失效时间
解决方案: 对参数加密, 生成 token , 判断 token 是否正确
解决方案: 使用 https , 用证书对数据进行加密, 即便数据被截取, 对黑客也没有意义
时间戳, 用于判断请求是否超时, 设置为30秒
其余参数加密而来, 保证数据不被篡改
接收加密过的用户密码, 用户密码永不返回
最好使用 https, 全部信息都会被加密
使用 common.php
统一处理参数过滤 G:tp5/application/api/controller/Common.php
java
<?php namespace app\api\controller; use think\Controller; use think\Request; use think\Validate; class Common extends Controller { protected $request; // 用来处理参数 protected $validater; // 用来验证数据/参数 protected $params; // 过滤后符合要求的参数 protected $rules = array( 'User'=>array(......); protected function _initialize() { parent::_initialize(); $this->request = Request::instance(); $this->check_time($this->request->only(['time'])); $this->check_token($this->request->param()); $this->params=$this->check_params($this->request->except(['time','token'])); }
自定义返回信息函数 G:tp5/application/api/controller/Common.php
数据库
/** * api 数据返回 * @param [int] $code [结果码 200:正常/4**数据问题/5**服务器问题] * @param [string] $msg [接口要返回的提示信息] * @param [array] $data [接口要返回的数据] * @return [string] [最终的json数据] */ public function return_msg($code, $msg = '', $data = []) { /*********** 组合数据 ***********/ $return_data['code'] = $code; $return_data['msg'] = $msg; $return_data['data'] = $data; /*********** 返回信息并终止脚本 ***********/ echo json_encode($return_data);die; }
验证 time
G:tp5/application/api/controller/Common.php
json
/** * 验证请求是否超时 * @param [array] $arr [包含时间戳的参数数组] * @return [json] [检测结果] */ public function check_time($arr) { if (!isset($arr['time']) || intval($arr['time']) <= 1) { $this->return_msg(400, '时间戳不正确!'); } if (time() - intval($arr['time']) > 60) { $this->return_msg(400, '请求超时!'); } }
验证token G:tp5/application/api/controller/Common.php
api
/** * 验证token(防止篡改数据) * @param [array] $arr [所有请求参数] * @return [json] [token验证结果] */ public function check_token($arr) { /*********** api传过来的token ***********/ if (!isset($arr['token']) || empty($arr['token'])) { $this->return_msg(400, 'token不能为空!'); } $app_token = $arr['token']; // api传过来的token /*********** 服务器端生成token ***********/ unset($arr['token']); $service_token = ''; foreach ($arr as $key => $value) { $service_token .= md5($value); } $service_token = md5('api_' . $service_token . '_api'); // 服务器端即时生成的token /*********** 对比token,返回结果 ***********/ if ($app_token !== $service_token) { $this->return_msg(400, 'token值不正确!'); } }
为每一个接口配置验证规则 G:tp5/application/api/controller/Common.php
如数组
protected $rules = array( 'User' => array( 'login' => array( 'user_name' => ['require', 'chsDash', 'max' => 20], 'user_pwd' => 'require|length:32', ), ), );
验证参数 G:tp5/application/api/controller/Common.php
安全
/** * 验证参数 参数过滤 * @param [array] $arr [除time和token外的全部参数] * @return [return] [合格的参数数组] */ public function check_params($arr) { /*********** 获取参数的验证规则 ***********/ $rule = $this->rules[$this->request->controller()][$this->request->action()]; /*********** 验证参数并返回错误 ***********/ $this->validater = new Validate($rule); if (!$this->validater->check($arr)) { $this->return_msg(400, $this->validater->getError()); } /*********** 若是正常,经过验证 ***********/ return $arr; }
验证码原理服务器
点击获取验证码
发送邮箱号到后台
后台生成邮箱验证码
用session保存验证码及邮箱
发送邮件
获取用户输入的验证码及邮箱
取出session保存的内容
对比验证
返回信息, 结束
配置路由 G:tp5/application/route.php
session
注意: get方式没有参数名, 因此要注意参数的顺序, 对号入座.
// 获取验证码 Route::get('code/:time/:token/:username/:is_exist','code/get_code');
参数过滤 G:tp5/application/api/controller/Common.php
在common.php里简单过滤, 具体验证放在code.php里
'Code' => array( 'get_code' => array( 'username' => 'require', 'is_exist' => 'require|number|length:1', ), ),
检测用户名 G:tp5/application/api/controller/Code.php
namespace app\api\controller; use phpmailer\phpmailer; use submail\messagexsend; class Code extends Common { public function get_code() { $username = $this->params['username']; $exist = $this->params['is_exist']; $username_type = $this->check_username($username); // 检查用户名, 决定用下面哪那个函数 switch ($username_type) { case 'phone': $this->get_code_by_username($username, 'phone', $exist); // 经过手机获取验证码 break; case 'email': $this->get_code_by_username($username, 'email', $exist); // 经过邮箱获取验证码 break; } } }
在 Common.php
中写一个判断用户名类型的函数
public function check_username($username) { /*********** 判断是否为邮箱 ***********/ $is_email = Validate::is($username, 'email') ? 1 : 0; /*********** 判断是否为手机 ***********/ $is_phone = preg_match('/^1[34578]\d{9}$/', $username) ? 4 : 2; /*********** 最终结果 ***********/ $flag = $is_email + $is_phone; switch ($flag) { /*********** not phone not email ***********/ case 2: $this->return_msg(400, '邮箱或手机号不正确!'); break; /*********** is email not phone ***********/ case 3: return 'email'; break; /*********** is phone not email ***********/ case 4: return 'phone'; break; } }
经过用户名(手机/邮箱)获取验证码 G:tp5/application/api/controller/Code.php
public function get_code_by_username($username, $type, $exist) { if ($type == 'phone') { $type_name = '手机'; } else { $type_name = '邮箱'; } /*********** 检测手机号/邮箱是否存在 ***********/ $this->check_exist($username, $type, $exist); /*********** 检查验证码请求频率 30秒一次 ***********/ if (session("?" . $username . '_last_send_time')) { if (time() - session($username . '_last_send_time') < 30) { $this->return_msg(400, $type_name . '验证码,每30秒只能发送一次!'); } } /*********** 生成验证码 ***********/ $code = $this->make_code(6); /*********** 使用session存储验证码, 方便比对, md5加密 ***********/ $md5_code = md5($username . '_' . md5($code)); session($username . '_code', $md5_code); /*********** 使用session存储验证码的发送时间 ***********/ session($username . '_last_send_time', time()); /*********** 发送验证码 ***********/ if ($type == 'phone') { $this->send_code_to_phone($username, $code); } else { $this->send_code_to_email($username, $code); } }
判断用户名(手机/邮箱)在数据库中是否应该存在(由于要分两种状况1,注册 2,修改)
当咱们注册的时候数据库里不能有,当咱们修改的时候数据库里必须有G:tp5/application/api/controller/Common.php
public function check_exist($value, $type, $exist) { $type_num = $type == "phone" ? 2 : 4; $flag = $type_num + $exist; $phone_res = db('user')->where('user_phone', $value)->find(); $email_res = db('user')->where('user_email', $value)->find(); switch ($flag) { /*********** 2+0 phone need no exist ***********/ case 2: if ($phone_res) { $this->return_msg(400, '此手机号已被占用!'); } break; /*********** 2+1 phone need exist ***********/ case 3: if (!$phone_res) { $this->return_msg(400, '此手机号不存在!'); } break; /*********** 4+0 email need no exist ***********/ case 4: if ($email_res) { $this->return_msg(400, '此邮箱已被占用!'); } break; /*********** 4+1 email need exist ***********/ case 5: if (!$email_res) { $this->return_msg(400, '此邮箱不存在!'); } break; } }
在 Code.php
中生成验证码
public function make_code($num) { $max = pow(10, $num) - 1; $min = pow(10, $num - 1); return rand($min, $max); }
只介绍邮箱发送验证码
开启邮箱smtp
php 开启php_openssl
G:tp5/application/api/controller/Code.php
public function send_code_to_email($email, $code) { $toemail = $email; $mail = new PHPMailer(); $mail->isSMTP(); $mail->CharSet = 'utf8'; // 设置字符集 $mail->Host = 'smtp.qq.com'; // smtp服务器 $mail->SMTPAuth = true; $mail->Username = "123456789@qq.com"; $mail->Password = "asd1151sad51dsa"; // 本身设置的smtp密码, 与登陆密码无关 $mail->SMTPSecure = 'ssl'; $mail->Port = 465; $mail->setFrom('123456789@qq.com', '接口测试'); $mail->addAddress($toemail, 'test'); $mail->addReplyTo('123456789@qq.com', 'lee'); $mail->Subject = "您有新的验证码!"; // 邮件标题 $mail->Body = "这是一个测试邮件,您的验证码是$code,验证码的有效期为1分钟,本邮件请勿回复!"; // 邮件内容 if (!$mail->send()) { $this->return_msg(400, $mail->ErrorInfo); } else { $this->return_msg(200, '验证码已经发送成功,请注意查收!'); } }