在Thinkphp官网发布 发布日期: 2019/10/18
但是不知道为什么,有时候,我好不容易写了一篇文章要发布,发送成功了,
但是在文章中心就是看不到,所以决定将部分文章转移至csdn
主要是为了方便自己查看
整理一下,前端向接口请求数据时的验证:比如我写了一个文章列表数据接口,那么我需要验证哪些规则,由此,我想了一下,除开签名验证,大概就只有ssl协议,白名单[ip,域名,url],黑名单[ip,域名,url],请求频率[eg:每10秒,最大请求数],设备【电脑,手机】,终端浏览器[微信,qq,…],设置请求方式并禁用curl获取数据[保证用户只能在页面使用ajax请求]。
如果不做请求方式的限制,那么就其他人就能通过curl方式获取到数据,于是写了下面一个例子
<?php
/**
* +----------------------------------------------------------------------
* | ThinkPHP [ WE CAN DO IT JUST THINK ]
* +----------------------------------------------------------------------
* | Copyright (c) 2019 ahai574 All rights reserved.
* +----------------------------------------------------------------------
* | Licensed ( ++++ahai574++++ )
* +----------------------------------------------------------------------
* | Author: 阿海 <764882431@qq.com>
* +----------------------------------------------------------------------
*/
/**
* 请求验证
* demo:
* $checkRequest = new CheckRequest();
* $checkRequest->setAjax('get',true);
* $checkRequest->setBlackList('10.0.0.155',false);
* $checkRequest->setWhiteList('10.0.0.15');
* $checkRequest->setDevice('mobile,pc',false);
* $checkRequest->setTerminal('uc,weixin',false);
* $checkRequest->setFrequent(10,3,true);
* $checkRequest->setSsl(false);
* $result = $checkRequest->check();
* if(!$result['result']) return json($result);
*/
namespace app\common\library;
use think\Controller;
use think\Exception;
use think\facade\Request;
class CheckRequest extends Controller
{
/**
* 开启 协议请求 必须是 https
* boolean
*/
private $isSsl = false;
/**
* 开启 验证请求 为 【页面内】ajax请求
* boolean
*/
private $isAjax = true;
/**
* ajax请求方式[eg:[post,get,put]]
* array
*/
private $ajax = [];
/**
* 是否开启 频繁请求验证
* boolean
*/
private $isFrequently = false;
/**
* 频繁请求 的 秒数
* int
*/
private $requestTime = 10;
/**
* 频繁请求 ($requestTime)10秒 内 的 最大访问次数
* int
*/
private $frequency = 50;
/**
* 是否开启白名单验证
* boolean
*/
private $isWhiteList = false;
/**
* ip白名单 或者 域名白名单
* [
* 完整的域名(eg:http:test.baidu.com),主域名(eg:baidu.com),
* 子域名[eg:test.baidu.com],url完整路径(eg:http://test.baidu.com/test/test.html)
* ]
* array
*/
private $whiteList = ['127.0.0.1'];
/**
* 是否开启黑名单验证 【优先级大于白名单】
* boolean
*/
private $isBlackList = false;
/**
* ip黑名单 或者 域名黑名单
* [
* 完整的域名(eg:http:test.baidu.com),主域名(eg:baidu.com),
* 子域名[eg:test.baidu.com],url完整路径(eg:http://test.baidu.com/test/test.html)
* ]
* array
*/
private $blackList = [];
/**
* 是否开启设备验证
* boolean
*/
private $isDevice = false;
/**
* 设备类型 手机[mobile] 电脑['pc']
* array
*/
private $device = ['mobile'];
/**
* 是否开启终端验证
* boolean
*/
private $isTerminal = false;
/**
* 终端浏览器类型 (eg:['weixin','qq','baidu','uc'])
* array
*/
private $terminal = ['weixin'];
/**
* 构造函数 constructor.
*/
public function __construct($config = [])
{
parent::__construct();
if(isset($config) && !is_array($config)){
throw new Exception("构造参数config必须是数组");
}
$this->isSsl = isset($config['isSsl']) && !empty($config['isSsl']) ? $config['isSsl'] : $this->isSsl;
$this->isAjax = isset($config['isAjax']) && !empty($config['isAjax']) ? $config['isAjax'] : $this->isAjax;
$this->ajax = isset($config['ajax']) && is_array($config['ajax']) ? $config['ajax'] : $this->ajax;
$this->isFrequently = isset($config['isFrequently']) && !empty($config['isFrequently']) ? $config['isFrequently'] : $this->isFrequently;
$this->requestTime = isset($config['requestTime']) && !empty($config['requestTime']) ? $config['requestTime'] : $this->requestTime;
$this->frequency = isset($config['frequency']) && !empty($config['frequency']) ? $config['frequency'] : $this->frequency;
$this->isWhiteList = isset($config['isWhiteList']) && !empty($config['isWhiteList']) ? $config['isWhiteList'] : $this->isWhiteList;
$this->whiteList = isset($config['whiteList']) && is_array($config['whiteList']) ? array_merge($this->whiteList, $config['whiteList']) : $this->whiteList;
$this->isBlackList = isset($config['isBlackList']) && !empty($config['isBlackList']) ? $config['isBlackList'] : $this->isBlackList;
$this->blackList = isset($config['blackList']) && is_array($config['blackList']) ? array_merge($this->blackList, $config['blackList']) : $this->blackList;
$this->isDevice = isset($config['isDevice']) && !empty($config['isDevice']) ? $config['isDevice'] : $this->isDevice;
$this->device = isset($config['device']) && is_array($config['device']) ? $config['device'] : $this->device;
$this->isTerminal = isset($config['isTerminal']) && is_array($config['isTerminal']) ? $config['isTerminal'] : $this->isTerminal;
$this->terminal = isset($config['terminal']) && is_array($config['terminal']) ? $config['terminal'] : $this->terminal;
}
/**
* 检测请求行为
* @return boolean
*/
public function check()
{
//验证黑名单--
if($this->isBlackList){
//ip,主域名,包含协议域名,请求的url完整路径, 主域名
$requestList = [Request::ip(),Request::host(),Request::domain(),Request::url(true),Request::rootDomain()];
if(!empty(array_intersect($this->blackList, $requestList)) ){
return ['result'=>false, 'errorMsg'=>'域名[ip]已被禁用', 'code'=>200];
}else{
//读取数据库黑名单表
$list = db('request_blacklist')
->whereOr('ip','eq',Request::ip())
->whereOr('domain','eq',Request::domain())
->whereOr('domain','eq',Request::host())
->whereOr('domain','eq',Request::rootDomain())
->whereOr('domain','eq',Request::url(true))
->find();
if(!is_null($list)){
return ['result'=>false, 'errorMsg'=>'域名[ip]已被禁用', 'code'=>200];
}
}
}
//验证ssl协议
if($this->isSsl){
if(!Request::isSsl()){
return ['result'=>false, 'errorMsg'=>'请求协议必须是https协议', 'code'=>200];
}
}
//验证是否ajax 必须是页面内的请求 [避开curl请求,浏览器直接输入地址]
if($this->isAjax){
if(empty($this->ajax)){
if(!Request::isAjax()){
return ['result'=>false, 'errorMsg'=>'非法请求', 'code'=>200];
}
}else{
if(!in_array(Request::method(), $this->ajax))
{
return ['result'=>false, 'errorMsg'=>'非法请求', 'code'=>200];
}
}
}
//验证请求频率
if($this->isFrequently){
$data = [
'method' => Request::method(),
'param' => json_encode(Request::param()),
'ip' => Request::ip(),
'url' => Request::url(true),
'http_user_agent' => $_SERVER['HTTP_USER_AGENT'],
'is_mobile' => Request::isMobile()?1:0,
'create_time' => time()
];
db('request_log')->insert($data);
$where = [
['ip','=',Request::ip()],
['create_time',">=",time()-$this->requestTime]
];
$list = db('request_log')->where($where)->count('id');
if($list > $this->frequency){
$data = [
'ip' => Request::ip(),
'domain' => Request::domain(),
'content' => '频繁请求',
'create_time' => time()
];
db('request_blacklist')->insert($data);
return ['result'=>false, 'errorMsg'=>'请求过于频繁,请稍后再进行请求', 'code'=>200];
}
}
//验证ip域名白名单
if($this->isWhiteList){
//ip,主域名,包含协议域名,请求的url完整路径, 主域名
$requestList = [Request::ip(),Request::host(),Request::domain(),Request::url(true),Request::rootDomain()];
if( empty(array_intersect($this->whiteList, $requestList)) ){
//读取数据库黑名单表
$list = db('request_whitelist')
->whereOr('ip','eq',Request::ip())
->whereOr('domain','eq',Request::domain())
->whereOr('domain','eq',Request::host())
->whereOr('domain','eq',Request::rootDomain())
->whereOr('domain','eq',Request::url(true))
->find();
if(is_null($list)){
return ['result'=>false, 'errorMsg'=>'域名[ip]不在白名单中', 'code'=>200];
}
}
}
//验证设备类型
if($this->isDevice){
$requestDevice = Request::isMobile() ? 'mobile' : 'pc';
if( !in_array($requestDevice, $this->device) ){
return ['result'=>false, 'errorMsg'=>'不支持当前设备', 'code'=>200];
}
}
//验证终端--
if($this->isTerminal){
$requestTerminal = $this->getBrowser();
if( !in_array($requestTerminal, $this->terminal) ){
return ['result'=>false, 'errorMsg'=>'不支持当前浏览器', 'code'=>200];
}
}
return ['result'=>true, 'msg'=>'请求通过', 'code'=>200];
}
/**
* 设置 ip 域名 白名单
* @param $blackList array
* @param $isBlackList boolean
*/
public function setBlackList($blackList = [], $isBlackList = true)
{
if(!is_array($blackList))
{
$blackList = explode(",", $blackList);
}
if(!is_bool($isBlackList))
{
throw new Exception("参数isBlackList必须是boolean");
}
$this->blackList = array_merge($this->blackList, $blackList);
$this->isBlackList = $isBlackList;
return $this;
}
/**
* 判断是否微信浏览器
*/
private function getBrowser()
{
$agent = $_SERVER['HTTP_USER_AGENT'];
if(strpos($agent, 'MicroMessenger') !== false)
{
return "weixin";
} else if(strpos($agent, 'QQBrowser') !== false)
{
return "qq";
} else if(strpos($_SERVER['HTTP_USER_AGENT'],'UCBrowser')!==false||strpos($_SERVER['HTTP_USER_AGENT'],'UCWEB')!==false)
{
return "uc";
} else if(strpos($agent, 'baidu') !== false)
{
return "baidu";
}else
{
return "other";
}
}
/**
* 设置是否开启 ajax请求验证
* @param $ajax array [post,get]
* @param $isAjax boolean
*/
public function setAjax( $ajax = [] ,$isAjax = true )
{
if(!is_array($ajax)){
$ajax = explode(",", $ajax);
}
if(!is_bool($isAjax)){
throw new Exception("参数isAjax必须是boolean类型");
}
$this->ajax = array_map('strtoupper',$ajax);
$this->isAjax = $isAjax;
return $this;
}
/**
* 设置是否开启 协议验证
* @param $isSsl boolean
*/
public function setSsl( $isSsl = true )
{
if(!is_bool($isSsl)){
throw new Exception("参数isSsl必须是boolean类型");
}
$this->isSsl = $isSsl;
return $this;
}
/**
* 设置 频繁请求的参数
* @param $isFrequently 是否开启验证
* @param $requestTime 设置验证的时间数
* @param $frequently 设置在规定的时间内的最大请求数
*/
public function setFrequent($requestTime = 10, $frequency = 50, $isFrequently = true )
{
if(!is_int($requestTime)||!is_int($frequency)){
throw new Exception("参数[requestTime,requestTime]必须是integer类型");
}
if(!is_bool($isFrequently)){
throw new Exception("参数frequently必须是boolean类型");
}
$this->requestTime = $requestTime;
$this->frequency = $frequency;
$this->isFrequently = $isFrequently;
return $this;
}
/**
* 设置 ip 域名 白名单
* @param $whiteList array
* @param $isWhiteList boolean
*/
public function setWhiteList($whiteList = [], $isWhiteList = true)
{
if(!is_array($whiteList))
{
$whiteList = explode(",", $whiteList);
}
if(!is_bool($isWhiteList))
{
throw new Exception("参数isWhiteList必须是boolean");
}
$this->whiteList = array_merge($this->whiteList, $whiteList);
$this->isWhiteList = $isWhiteList;
return $this;
}
/**
* 设置 设备类型
* @param $device array | string
* @param $isDevice 是否开启验证
*/
public function setDevice($device = [], $isDevice = true)
{
if(!is_array($device))
{
$device = explode(",", $device);
}
if(!is_bool($isDevice))
{
throw new Exception("参数isDevice必须是boolean");
}
$this->device = $device;
$this->isDevice = $isDevice;
return $this;
}
/**
* 设置 设备类型
* @param $terminal array | string
* @param $isTerminal 是否开启验证
*/
public function setTerminal($terminal = [], $isTerminal = true)
{
if(!is_array($terminal))
{
$terminal = explode(",", $terminal);
}
if(!is_bool($isTerminal))
{
throw new Exception("参数isTerminal必须是boolean");
}
$this->terminal = $terminal;
$this->isTerminal = $isTerminal;
return $this;
}
}