PHP开发api接口安全验证

APP接口安全验证方法,基本有这三类。

1.请求头里带用户username和password,到服务器端做验证,通过才继续下边业务逻辑。

有点:防止了服务器端api被随意调用。

缺点:每次都交互用户名和密码,交互量大,且密码明文传输不安全。

 

2.第一次请求,要求username和password,验证通过,种cookie到客户端,app保存cookie值。

每次请求带上cookie。

点评:和pc上浏览器认证的原理一样了。

 

3.制定一个token生成规则,按某些服务器端和客户端都拥有的共同属性生成一个随机串,客户端生成这个串,服务器收到请求也校验这个串。

缺点:随机串生成规则要保密。


第二种方法:

url地址:http://www.xxx.com/api/user?key="JG56sdfk&$7ghjkdsj"

post参数:p=Base64加密后的json字符串


登录函数:

public function login(){
	$p = I('p');
	$p = base64_decode($p);
	$req_arr = json_decode($p,true);
	
	//检查服务器是否存有cookie信息
	$User = M("User");
	$map['user_account'] 	= $req_arr['user_account'];
	$map['user_pwd'] 		= md5( $req_arr['user_pwd'] );
	$res = $User->where($map)->find();

	$msg['rtnflag'] = "F";
	$msg['rtninfo'] = "查询出错!";
	if($res === null || !is_array($res)){
		$msg['rtnflag'] = "F";
		$msg['rtninfo'] = "用户名或密码错误!";
		return $msg;
	}
	if( is_array($res) ){
		$msg['rtnflag'] = "T";
		$msg['rtninfo'] = "";
		$msg['rtnbody'] = $res;
		$msg['rtnbody']['key'] = base64_encode( $res['user_tel'].time() );
	}

        //把key写入数据库当作cookie
	$UserKey = M("UserKey");
	$data['user_id']	= $msg['rtnbody']['user_id'];
	$data['user_key'] 	= $msg['rtnbody']['key'];
	$data['create_time']= time();
	$data['expiration_time'] = strtotime("+7 day" , $data['create_time'] );

	$UserKey->where( array('user_id'=>$data['user_id']) )->delete();
	$res = $UserKey->data($data)->add();
	if(!$res){
		$msg['rtnflag'] = "F";
		$msg['rtninfo'] = "写入数据库";
	}

	return $msg;
}

登录验证:

function getKey(){
	//$key = I("key");
	$key = $_GET["key"];

	$msg['rtnflag'] = "F";
	$msg['rtninfo'] = "没有登录,或登录超时!";

	$res = M("UserKey")->where( array("user_key"=>$key) )->find();
	if( !is_array( $res ) ){
		return $msg;
	}

	if( $res['expiration_time'] < time() ){
		return $msg;
	}

	$info = base64_decode( $res['user_key'] );
	//p($info);
	$data['user_account'] =	substr($info,0,11);
	$data['create_time'] =	substr($info,11,10);

	$res = M("User")->where( array("user_account"=>$data['user_account']) )->find();
	if(!is_array( $res )){
		$msg['rtnflag'] = "F";
		$msg['rtninfo'] = "没有该用户!";
		return $msg;
	}

	$data['rtnflag'] = "T";
	$data['user_id'] = $res['user_id'];
	$data['user_community_id'] = $res['user_community_id'];

	return $data;
}





第三种方法:

在实际工作中,使用PHP写api接口是经常做的,PHP写好接口后,前台就可以通过链接获取接口提供的数据,而返回的数据一般分为两种情况,xml和json,在这个过程中,服务器并不知道,请求的来源是什么,有可能是别人非法调用我们的接口,获取数据,因此就要使用安全验证。

 

验证原理示意图

1.png

 

从图中可以看得很清楚,前台想要调用接口,需要使用几个参数生成签名。

 

时间戳:当前时间

随机数:随机生成的随机数

口令:前后台开发时,一个双方都知道的标识,相当于暗号

算法规则:商定好的运算规则,上面三个参数可以利用算法规则生成一个签名。

前台生成一个签名,当需要访问接口的时候,把时间戳,随机数,签名通过URL传递到后台。后台拿到时间戳,随机数后,通过一样的算法规则计算出签名,然后和传递过来的签名进行对比,一样的话,返回数据。

 

算法规则

 

在前后台交互中,算法规则是非常重要的,前后台都要通过算法规则计算出签名,至于规则怎么制定,看你怎么高兴怎么来。

 

我这个算法规则是

 

时间戳,随机数,口令按照首字母大小写顺序排序

然后拼接成字符串

进行sha1加密

再进行MD5加密

转换成大写。

前台

 

这里我并没有实际的前台,直接使用一个PHP文件代替前台,然后通过CURL模拟GET请求。我使用的是TP框架,URL格式是pathinfo格式。

 

源代码

 

<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2017/3/16 0016
 * Time: 15:56
 */
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
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2017/3/16 0016
 * Time: 16:01
 */
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"}"


如果对安全有更高的要求,就要使用ras算法和SSL协议了。

 

------------------------------

现在2018年5月16号, 已经放弃在链接后面跟着key了,改成所有验证用的信息都存储在header请求中。

 

修改时间 2018-05-16

真诚赞赏,手留余香
赞赏
随机推荐
CentOS 磁盘空间相关命令
ul li格式的分页样式
NETFLIX纪录片: 设计的艺术 - 插画
CSS实现按钮的特效:流光按钮,冷光按钮
javascript获取当前时间
入门 Webpack,看这篇就够了
白无常「两小时」
php连贯操作实现
Webpack 使用 HtmlWebpackPlugin 简化了HTML文件的创建
PHP ExcelReader使用教程