一、什么是API
API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。Web API是网络应用程序接口。包含了广泛的功能,网络应用通过API接口,可以实现存储服务、消息服务、计算服务等能力,利用这些能力可以进行开发出强大功能的web应用。
基于互联网的应用正变得越来越普及,在这个过程中,有更多的站点将自身的资源开放给开发者来调用。对外提供的API 调用使得站点之间的内容关联性更强,同时这些开放的平台也为用户、开发者和中小网站带来了更大的价值。
开放是目前的发展趋势,越来越多的产品走向开放。目前的网站不能靠限制用户离开来留住用户,开放的架构反而更增加了用户的粘性。在Web 2.0的浪潮到来之前,开放的API 甚至源代码主要体现在桌面应用上,而现在越来越多的Web应用面向开发者开放了API。具备分享、标准、去中心化、开放、模块化的Web 2.0
站点,在为使用者带来价值的同时,更希望通过开放的API 来让站点提供的服务拥有更大的用户群和服务访问数量。站点在推出基于开放API 标准的产品和服务后,无需花费力气做大量的市场推广,只要提供的服务或应用出色易用,其他站点就会主动将开放API 提供的服务整合到自己的应用之中。同时,这种整合API 带来的服务应用,也会激发更多富有创意的应用产生。为了对外提供统一的API 接口,需要对开发者开放资源调用API 的站点提供开放统一的API接口环境,来帮助使用者访问站点的功能和资源。当然,开放API 的站点为第三方的开发者提供良好的社区支持也是很有意义的,这有助于吸引更多的技术人员参与到开放的开发平台中,并开发出更为有趣的第三方应用。视频云技术提供商CC视频开放API接口,用户可以在自己的网站后台轻松完成视频的上传、视频播放控制操作,并可批量获取视频及平台信息。
二、Web API的接口访问分类
1、一个是使用用户令牌,通过Web API接口进行数据访问。这种方式,可以有效识别用户的身份,为用户接口返回用户相关的数据,如包括用户信息维护、密码修改、或者用户联系人等与用户身份相关的数据。
2、一种是使用安全签名进行数据提交。这种方式提交的数据,URL连接的签名参数是经过安全一定规则的加密的,服务器收到数据后也经过同样规则的安全加密,确认数据没有被中途篡改后,再进行数据修改处理。因此我们可以为不同接入方式,如Web/APP/Winfrom等不同接入方式指定不同的加密秘钥,但是秘钥是双方约定的,并不在网络连接上传输,连接传输的一般是这个接入的AppID,服务器通过这个AppID来进行签名参数的加密对比,这种方式,类似微信后台的回调处理机制,它们就是经过这样的处理。
3、一种方式是提供公开的接口调用,不需要传入用户令牌、或者对参数进行加密签名的,这种接口一般较少,只是提供一些很常规的数据显示而已。
三、Web API使用token的实现
在API开发过程会经常使用token来完成请求签权,以防止api接口被恶意调用
token校验:
- <?php
- namespace Client\Controller;
- use Think\Controller;
-
- class ClientController extends Controller{
- const TOKEN = 'API';
- //模拟前台请求服务器api接口
- public function getDataFromServer(){
- //时间戳
- $timeStamp = time();
- //随机数
- $randomStr = $this -> createNonceStr();
- //生成签名
- $signature = $this -> arithmetic($timeStamp,$randomStr);
- //url地址
- $url = "http://www.apitest.com/Server/Server/respond/t/{$timeStamp}/r/{$randomStr}/s/{$signature}";
- $result = $this -> httpGet($url);
- dump($result);
- }
-
- //curl模拟get请求。
- private function httpGet($url){
- $curl = curl_init();
-
- //需要请求的是哪个地址
- curl_setopt($curl,CURLOPT_URL,$url);
- //表示把请求的数据已文件流的方式输出到变量中
- curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
-
- $result = curl_exec($curl);
- curl_close($curl);
- return $result;
- }
-
- //随机生成字符串
- private function createNonceStr($length = 8) {
- $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- $str = "";
- for ($i = 0; $i < $length; $i++) {
- $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
- }
- return "z".$str;
- }
-
- /**
- * @param $timeStamp 时间戳
- * @param $randomStr 随机字符串
- * @return string 返回签名
- */
- private function arithmetic($timeStamp,$randomStr){
- $arr['timeStamp'] = $timeStamp;
- $arr['randomStr'] = $randomStr;
- $arr['token'] = self::TOKEN;
- //按照首字母大小写顺序排序
- sort($arr,SORT_STRING);
- //拼接成字符串
- $str = implode($arr);
- //进行加密
- $signature = sha1($str);
- $signature = md5($signature);
- //转换成大写
- $signature = strtoupper($signature);
- return $signature;
- }
- }
- <?php
- namespace Server\Controller;
- use Think\Controller;
-
- class ServerController extends Controller{
- const TOKEN = 'API';
-
- //响应前台的请求
- public function respond(){
- //验证身份
- $timeStamp = $_GET['t'];
- $randomStr = $_GET['r'];
- $signature = $_GET['s'];
- $str = $this -> arithmetic($timeStamp,$randomStr);
- if($str != $signature){
- echo "-1";
- exit;
- }
- //模拟数据
- $arr['name'] = 'api';
- $arr['age'] = 15;
- $arr['address'] = 'zz';
- $arr['ip'] = "192.168.0.1";
- echo json_encode($arr);
- }
-
- /**
- * @param $timeStamp 时间戳
- * @param $randomStr 随机字符串
- * @return string 返回签名
- */
- public function arithmetic($timeStamp,$randomStr){
- $arr['timeStamp'] = $timeStamp;
- $arr['randomStr'] = $randomStr;
- $arr['token'] = self::TOKEN;
- //按照首字母大小写顺序排序
- sort($arr,SORT_STRING);
- //拼接成字符串
- $str = implode($arr);
- //进行加密
- $signature = sha1($str);
- $signature = md5($signature);
- //转换成大写
- $signature = strtoupper($signature);
- return $signature;
- }
- }
- <?php
- namespace Server\Controller;
- use Think\Controller;
-
- class ServerController extends Controller{
- const TOKEN = 'API';
-
- //响应前台的请求
- public function respond(){
- //验证身份
- $timeStamp = $_GET['t'];
- $randomStr = $_GET['r'];
- $signature = $_GET['s'];
- $str = $this -> arithmetic($timeStamp,$randomStr);
- if($str != $signature){
- echo "-1";
- exit;
- }
- //模拟数据
- $arr['name'] = 'api';
- $arr['age'] = 15;
- $arr['address'] = 'zz';
- $arr['ip'] = "192.168.0.1";
- echo json_encode($arr);
- }
-
- /**
- * @param $timeStamp 时间戳
- * @param $randomStr 随机字符串
- * @return string 返回签名
- */
- public function arithmetic($timeStamp,$randomStr){
- $arr['timeStamp'] = $timeStamp;
- $arr['randomStr'] = $randomStr;
- $arr['token'] = self::TOKEN;
- //按照首字母大小写顺序排序
- sort($arr,SORT_STRING);
- //拼接成字符串
- $str = implode($arr);
- //进行加密
- $signature = sha1($str);
- $signature = md5($signature);
- //转换成大写
- $signature = strtoupper($signature);
- return $signature;
- }
- }
测试结果为:string(57) "{"name":"api","age":15,"address":"zz","ip":"192.168.0.1"}"
四、PHP 开发API接口签名验证
来源:https://www.cnblogs.com/dcb3688/p/4608008.html
就安全来说,所有客户端和服务器端的通信内容应该都要通过加密通道(HTTPS)传输,明文的HTTP通道将会是man-in-the- middle及其各种变种攻击的温床。所谓man-in-the-middle攻击简单讲就是指恶意的黑客可以在客户端和服务器端的明文通信通道上做手 脚,黑客可以监听通信内容,偷取机密信息,甚至可以篡改通信内容,而通过加密后的通信内容理论上是无法被破译的。
URL签名生成规则
API的有效访问URL包括以下三个部分: |
签名算法如下:
1. 对所有请求参数进行字典升序排列; |
注意:请保证HTTP请求数据编码务必为UTF-8格式,URL也务必为UTF-8编码格式。
举个实例:
PHP服务端先要给开发者(APP)分配一个appid与appsecret (正常情况下,开发者要到服务提供商的官网申请),作为客户端,需要保留好官方颁发的appid & appsecret
appid会在请求中作为一个应用标识参与接口请求的参数传递,appsecret 将作为唯一不需要参数传递,但是它将作为验证当前请求的关键参数,只有应用开发者和颁发的服务端才知道。由于签名是依靠同样的算法加密实现,因此,应用端和服务端可以计算出相同的签名值,签名实际意义在于服务端对客户端的访问身份认证。在某种意义上签名机制有点类似用公钥方法签名,用每个应用对应的私钥值来解密,只是这种解密过程实质就是核对签名参数值的过程。假设分配:
$appid=5288971; $appsecret= 'r5e2t85tyu142u665698fzu';
移动客户端,需要请求服务列表(以下代码可以为java或sf等移动端编写)请求地址: http://web.com/server/list
参数:
$array=[ 'appid'=>5288971, 'menu'=>'客户服务列表', 'lat'=>21.223, 'lng'=>131.334 ];
对应签名算法
// 1. 对加密数组进行字典排序 foreach ($array as $key=>$value){ $arr[$key] = $key; } sort($arr); //字典排序的作用就是防止因为参数顺序不一致而导致下面拼接加密不同 // 2. 将Key和Value拼接 $str = ""; foreach ($arr as $k => $v) { $str = $str.$arr[$k].$array[$v]; } //3. 通过sha1加密并转化为大写 //4. 大写获得签名 $restr=$str.$appsecret; $sign = strtoupper(sha1($restr));
将生产的sign签名一起写入array中,通过约定好的method方式发送参数到请求接口
$array['sign']=$sign;
打印$array
Array ( [appid] => 5288971 [menu] => 客户服务列表 [lat] => 21.223 [lng] => 131.334 [sign] => C096D7811E944386CE880597BA334A5AB640B088 )
客户端将数据封装xml或Json发送到服务端,服务端先解析
{"appid":5288971,"menu":"\u5ba2\u6237\u670d\u52a1\u5217\u8868","lat":21.223,"lng":131.334,"sign":"C096D7811E944386CE880597BA334A5AB640B088"}
$serverArray= json_decode($json,TRUE);
服务端查询appid对应的密钥
$model=Model::find()->where("appid=:appid")->params([":appid"=>$serverArray['appid']])->one(); if($model){ $serverSecret=$model->appsecret; }
按照相同的字典排序与算法生成服务端的$sign ,判断$sign 是否相同。
$clientSign=$array['sign']; unset($serverArray['sign']); #生成服务端str $serverstr = ""; foreach ($serverArray as $k => $v) { $serverstr = $str.$k.$v; } $reserverstr=$str.$serverSecret; $reserverSign = strtoupper(sha1($reserverstr)); if($clientSign!=$reserverSign){ die('非法请求'); }else{ // your code continue; }
在仅适用短信登录做手机端app时,可以设置secret的过期时间,短信登录后,保存appid(userid)与密钥secret,每当用户打开APP时,先联网请求登录是否过期,过期重新短信登录获取新的secret。
附加:
有时,我们使用hash_hmac进行加密(我们项目中使用……
/* * 生成签名,$args为请求参数,$key为私钥 */ function makeSignature($args, $key) { if(isset($args['sign'])) { $oldSign = $args['sign']; unset($args['sign']); } else { $oldSign = ''; } ksort($args); $requestString = ''; foreach($args as $k => $v) { $requestString .= $k . '=' . urlencode($v); } $newSign = hash_hmac("md5",strtolower($requestString) , $key); return $newSign; }
javascript 进行加密签名
微信小程序开发的时候,为了数据的安全,也可以用js对数据签名,发送服务器进行效验认证.
先引入md5.js
<script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.js"></script>
var postData =<?php echo json_encode($data); ?>; var token ='<?php echo token; ?>'; /** * json 排序 * 先排序再toLower,所以Did 在appid 之前 */ function jsonSort(jsonObj) { let arr = []; for (var key in jsonObj) { arr.push(key) } arr.sort(); let str = ''; for (var i in arr) { str += arr[i].toLowerCase() + "=" + jsonObj[arr[i]].toLowerCase(); } return str } strData = jsonSort(postData); var sign = md5(strData, token); postData['sign'] = sign; console.log(postData);
更多请敬请关注