移动端后台开发 (加密验证--非通用接口篇)

前言

上次说完了通用接口加密验证,这次来说一下非通用接口的验证,在说之前我们来看一下移动端需要通过非通用接口时的表字段。

一.表字段

id 主键ID
uid 用户ID
deviceid 设备唯一识别码
token 加密使用字段(可以使用UUID1)
token_time token过期时间

二、非通用接口

回忆下这段代码,来看看非通用方法里做了什么

private function verify($request)
{

    if($request->path()=='login'){
       // 通用方法
        return  self::$verify->common($request);
    }else{
       // 非通用方法 
        return self::$verify->proprietary($request);
    }
    return false;
}

proprietary里都做了一下操作

public function proprietary($request)
{
    if($request->all()){
        $data = $request->all();
        // 时间验证
        $ckTime = $this->checkTime($data['time']);
        if(!$ckTime) return 'SN002';
        // 因为是非通用接口,这里的id,就需要是用户表里的ID,
        if(!isset($data['id'])) return "SN004";
        // 根据版本设计不同的验证
        switch ($data['version']){
            case $data['version'] >'1.4' && $data['version']<'2.0':
                $temp = $this->checkProprietary_v1($request);
                break;
            default:
                $temp = $this->checkProprietary_v2($request);
                break;
        }
        if($temp){
            switch ($temp){
                case 'SN007':   // 用户不存在
                    return 'SN007';
                    break;
                case 'SN008':  // 其他设备登录
                    return 'SN008';
                    break;
                case 'SN009':  // token 过期
                    return 'SN009';
                    break;    
                default:      // 验证成功
                    return 'SN200';
                    break;
            }
        }
            return "SN005";
    }
    return false;
}

在上边操作中,把请求传送到了checkProprietary_v1方法中,在看这个方法之前先看一下user方法

//这里根据key从缓存中读取数据,如果不存在就去数据库中获取数据,然后存在缓存中
public function user($id)
{
    $key='USER:MESSAGE:'.$id;
    if(!\Redis::exists($key)){
        $userJson = Common::curl('/getToken',['id'=>$id],1);
        $user = json_decode($userJson,1);
        if($user['ServerNo'] == 'SN200'){
            \Redis::hMset($key,$user['ResultData']);
            return $user['ResultData'];
        }else{
            return false;
        }
    }
    return \Redis::hGetall($key);
}

现在再看checkProprietary_v1方法

private function checkProprietary_v1($request)
{

    $data = $request->all();
    $path = $request->path();
    $param = $data['param'];
    $id = $data['id'];
    $signature = $data['signatures'];
    $time = $data['time'];
    $deviceid = $data['deviceid'];

    $user = $this->user($id);
    if(!$user) return 'SN007';  // 用户不存在
    $tokentime = $user['token_time']; //获取token的过期时间
    if($deviceid != $user['deviceid']){
       return 'SN008';  // 识别码不同 抛其他设备登录 并重新登录
    }
    if($time > $tokentime){
        return 'SN009';     // token超时 重新登录
    }
    $token = $user['token']; // 获取用户的token
    //需注意生成的token为32位的字符串
    $hashs = [
        [1,2,23,28,23,45],
        [6,8,19,25,30,31],
        [0,25,31,3,4,8],
        [2,31,0,9,3,17],
        [29,2,1,17,21,26],
        [10,5,18,9,2,3],
        [5,10,15,17,18,22],
        [8,20,22,37,19,21],
        // 可以继续定义数组
    ];
    $strs =substr($token,4,1);
    $strs.=substr($token,5,1);
    $strs.=substr($token,9,1);
    $code = hexdec($strs);
    $str1 = $code%8;
    $arr =$hashs["$str1"];
    $m = null;
    foreach($arr as $v){
        $m.= substr($token,$v,1);
    }
    $str = md5($path.$time.$id.$param.$m);
    if($signature == $str){
        return 'SN200';
    }else{
        return false;
    }
}

解释:

用户每次登陆时,账号密码验证通过将以下字段存入数据库,如UID存在则进行修改

uid // 用户ID 唯一
deviceid // 设备唯一识别码,每次登陆时都需获取一次
token // 每次登陆都需要重新生成一条
token_time // 需改变 当前时间+希望保持的时间

注意点:

1.登陆成功后,一定要报token返回给移动端
2.在移动端也要保存$hashs数组,并使用一样的算法

最后贴出一段根据状态码返回信息提示的代码

 public function handle($request, Closure $next)
{
    $time = time();
    switch($this->verify($request))
    {
        case "SN200":
            $temp =  $next($request);
            // 封装
            return $temp;
            break;
        case "SN001":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN001','ResultData'=>'Server internal error!']);
            break;
        case "SN002":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN002','ResultData'=>'Request timeout!']);
            break;
        case "SN003":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN003','ResultData'=>'Version number exception!']);
            break;
        case "SN004":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN004','ResultData'=>'Global user ID can not be null!']);
            break;
        case "SN005":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN005','ResultData'=>'Signature error!']);
            break;
        case "SN007":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN007','ResultData'=>'user not!']);
            break;
        case "SN008":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN008','ResultData'=>'Other devices login!']);
            break;
        case "SN009":
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN009','ResultData'=>'token time out!']);
            break;
        default:
            return response()->json(['serverTime'=>$time,'ServerNo'=>'SN006','ResultData'=>'No access!']);
    }

}
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 3
leo

Laravel内置了Ramsey\Uuid\Uuid类,不用自己写代码生成……

7年前 评论

后面 case 里的 return 后继续接 break,看的有点尴尬。

7年前 评论

Uuid的文档介绍在哪一部分

7年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!