实现接口权限验证——验签

1,126 阅读3分钟

实现接口权限验证

开发用户接口实例之前,还需要设计一套严格的接口验证方法,保证 API 接口的安全
性。常见的接口验证一般包含以下几种

  • 请求时间:接口时效性验证。为了防止大 的重复请求, 可以设置每次请求的时效 小到以秒为单位, 大到可以是半个 小时
  • 验签:参数完整性验证。一般接口攻击, 都会截获请求,附带额外参数进行攻击,需要进行过滤验证。此时就需要客户端把所有的请求参 数根据一个特定的算法,生成一个签名字符串,并在请求时一并发送。服务器接收到这个 签名字符串后进行验证。
  • token:用户唯一Token 。用户每次登录都会生成唯 Token 信息,注销或者超时会话时 Token 会被注销,防止被非法获取

根据以上设计思路,本实例设计基于时效和参数 效验的权限验证方法。接口验证流程如图所示 下面以 ThinkP 框架为例,查看实现接口验 证父类的步骤与方法。

在这里插入图片描述

下面以 ThinkP 框架为例,查看实现接口验
证父类的步骤与方法。

实现权限验证基类

在application 目录下创建名为 api 应用模块 随后新建控制器 Api lp 作步骤如下。

(1 )定义返回值方法

api.php 控制器文件中 新增以下方法

/*
* 数据返回
* @param string $code HTTP CODE 
* @param string message 提示信息
* @param array $data 返回数据
*/ 
public function return_msg($code="200", $message="成功", $data= [)){
		header('Content-Type:application/json');//设置返回类型
		http_response_code($code);//设置返回头部
		$return['code'] = $code ; 
		$return['message'] =$message ; 
		if(!empty($data)){ 
			$return['data '] =$data; 
		}  
		exit(json_encode($return, JSON_UNESCAPED_UNICODE));
}

此方法用来定义返回信息操作,其中定义了 HTTP Header 类型和编码,代码如下:

header(‘Content-Type:application/json’);//设置返回类型
http_response_code($code);//设置返回头部

把传入的参数组装为数组,返回 JSON 格式数据,代码如下

$return['code'] = $code ; 
$return['message'] =$message ; 
if(!empty($data)){ 
	$return['data '] =$data; 
}  
exit(json_encode($return, JSON_UNESCAPED_UNICODE));

(2 )定义接口时效性验证方法

定义接口 验证方法,代码如下:

/** 
*检测接口 是否超时
* @pa ram $arr 
*/
public function check_time($arr) { 
//设置返回类型
//设置返回头部
if(!isset($arr["time"])|| intval($arr["time"])<=1){
	$this_return_msg(400, '时间戳错误 !');
} 
if(time()-  intval($arr["time"]) >$this->timeout_second){
	$this_return_msg(400, ' 请求超时!');
} 
 
 

该方法中进行了两部分验证 时间参数(time )是否非空验证和是否超时验证。如果
任意一项无法匹配,方法都会返回错误信息。其 $this- timeout_second 属性,定义了接
口时效是多少秒,在类中定义为属性,代码如下 :

//默认接口的有效期为 60
protected $timeout_second = 60 ;

(3 )定义生成接口签名方法

为了防止请求中带有其他恶意的参数 需要根据原始数据,定义签名生成方法,代
如下:

/**
*构建请求签名
* @param pa ram 
* @return string 
*/
public function buildSign($param){
	unset($param["sign"]);//sign 字段不需要加入签名算法
	unset($param["time "]);//time 字段不需要加入签名算法
	ksort($param);//键值对的 key 按照升序排序
	$str=implode("",$param);//请求参数值拼接成字符串
	$sign =md5(md5($str).$this->sign_key);//执行加密
	return $sign ;
}   

buildSign()方法中,生成签名的算法主要有以下3步

  • 对请求参数 数组进行排序 使用 PHP 中的 ksort() 方法进行排序。
  • 把排序结果转换为字符串。
  • 使用 md5()方法和默认签名的 Key 进行签名生成的运算。
    其中 $this- sign_key 属性值在类中定义 代码如下。 签名 Key 可以是动态的,只要能和客户端在生成签名时保持一致即可。
//执行 key
private $sign_key = 'ThinkPHPS ';

(4)定义签名验证方法

该方法把请求接口的参数值,先进行排序和字符串转换,然后再进行生成签名处理。
返回值中所带的签名,如果和验证参数值生成的签名值保持 ,则通过验证,反之则
示错误信息。该方法定义如下

/** 
*验证请求签名是否正确
*@param $param 
*/
public function check_sign($param ){
	if(!issert($param["sign"])||!$param["sign"]){
		$this_return_msg(400, ' 签名不能为空!');
	}
	if( $param["sign"]!== $this->buildSign($param)){
		$this_return_msg(400, '签名错误!');
	}
}

(5)在初始化方法中调用验证

验证方法定义完成后 需要在控制器的初始化方法中调用,代码定义如下:

//初始化方法
public function _initialize(){
	parent::_initialize();
	//获取请求对象
	$this->request= Request::instance();
	//是否开启 API 权限验证(方便测试)
	if(confid("api_auth")){
			//验证时间戳是否超时
			$this->check_time($this->request->only(["time"]));
			//验证签名是否正确
			$this->check_sign($this->request->param());
	}
} 

需要注意的是,初始化方法中不仅获取了请求对象

 $this->request= Request::instance();

还使用了系统配置,来定义是否开启验证(方便开发者模式)

if(confid("api_auth"))

application api config.php 文件中,定义如下

<?php 
II API 块设计
return [ 
//应用命名空间
	'api_auth' => true 
]

访问地址 若效果如图,则说明 API 接口的全局验证成功
在这里插入图片描述

奇奇怪怪的只是又增加了呢

为了快速生成控制器类 在命令行下切换到应用根目 录下,执行 以下命令:

php think make:controller api/User 

系统会自动生成 User 资源控制器类,并创建对应资源操作的几个方法。为了在URL 中可以访问资源,则需要定义资源路由规则。在 application 目录下的 route,php 文件
中,追加以下内容:

Route::resource('users','api/User');

按资源路由的注册方法,定义 个名为 users 的路由, 其实内部会自动注册 个路由
规则,详细对应关系如表

请求表示请求类型路由规则对应操作方法描述
indexGETusersindex显示用户列表
createGETusers/createcreate新增用户页面
savePOSTuserssave保存用户信息
readGETusers/:idread查看用户信息
editGETusers/:id/editedit编辑用户页面
updatePUTusers/:idupdate更新用户信息
deleteDELETEusers/:iddelete删除用户