网站首页 » 网络 » 加密方式之自己正使用无状态加密

加密方式之自己正使用无状态加密

July 22, 2020 网络

传统加密方式-有状态加密

用户登录后,我们分配给用户一个token, 然后将token 放在redis中一份,放在数据库中一份; 当用户携带token 访问接口时,我们一般会做以下判断:

token是否正确
token是否过期

通过以上的判断,我们可以确认用户的有效性。这中间就涉及到一个问题,我们需要取出自己保存的token, 先从redis中获取,如果redis中key过期或者redis宕机,我们就要从数据库中获取,无论从哪获取都涉及到IO。

这种方式就是有状态的加密。

无状态加密

用户登录之后,我们返回用户一些用于加密的字段, 用户访问接口时,使用接口入参,加密字段和固定字段key进行加密,生成不可你解析token ,一般使用md5加密, 服务器端拿到token,入参后, 通过同样的规则, 使用接口入参,加密字段和固定字段key进行加密得到tokenVerify, 对比token 和tokenVerify , 相同则用户有效,不同则用户无效。 无需访问redis和数据库。

这种方式就是无状态的加密。

下面上代码,在实际应用中看下实现方式

首先我们有一个BaseController是基类控制器,做用户验证等基础工作

<?php
/**
 *
 * File Base
 * author mselect
 * DateTime 2019-12-01
 * @return
 */

namespace app\wxmini\controller;


use think\App;
use think\exception\HttpResponseException;
use think\Response;

class BaseController extends \think\Controller
{
    //用户ID
    protected $user_id = 0;
    //请求头
    protected $header = array(
        'Access-Control-Allow-Origin' => '*',
        'Access-Control-Allow-Headers' => 'X-Requested-With,Content-Type,Api-Token,Api-Version,Api-Timestamp,Api-User,Api-Sign',
        'Access-Control-Allow-Methods' => 'GET,POST,OPTIONS',
    );
    //original 准过滤白名单
    public $whiteOrigin = array();
    protected $whiteList = array();
    //api版本号
    protected $versionToKey = [
        '1.0.0' => '77fruit',
    ];

    protected $codeMark = [
        'SUCCESS' => 1,                 //返回成功
        'ERROR' => -1,                  //返回失败
        'NOMORE' => -50,                //没有要加载的更多数据
        'NODATA' => -100,               //没有数据
        'NOTLOGIN' => -1000,            //未登录
        'DATAERROR' => -2000,           //数据错误
        'SIGNEMPTY' => -2001,           //签名为空
        'SIGNERROR' => -2002,           //签名错误
        'LOGINERROR' => -2010,          //登陆失败
        'SECURITYERROR' => -10000,        //非法登陆
        'SYSTEMERROR' => -2333,           //系统错误
    ];


    public function __construct(App $app = null)
    {
        parent::__construct($app);
        if(in_array($this->request->action(), $this->whiteList)){
            return true;
        }

        $this->_checkPostData();
        $this->_initUser();
    }

    /**
     * 用户信息初始化
     * Function _initUser
     * author mselect
     * DateTime 2019-12-01
     */
    public function _initUser(){
        $token = (string)$this->request->header('Api-Token', '');
        $user_id = (string)$this->request->header('Api-User', 0);
        $timestamp = (string)$this->request->header('Api-Timestamp', '');
        $version = (string)$this->request->header('Api-Version', '');

        if( empty($this->versionToKey[$version])){
            $this->outPut('SECURITYERROR', '非法访问');
        }

        if($token != '' && $user_id >0 && $timestamp != '' && $version != ''){
            //均带着登陆所需参数
            $newMd5 = md5($this->versionToKey[$version] . $timestamp . $user_id . $version );

            if($newMd5 === $token){
                //符合登陆规则
                $this->user_id = $user_id;
            }
        }

    }


    /**
     * 检查Post数据
     * Function checkPostData
     * author mselect
     * DateTime 2019-12-01
     */
    private function _checkPostData(){
        $post = $this->request->post();
        $version = (string)$this->request->header('Api-Version', '');

        if( empty($post)){
            $string = '';
        }else {
            ksort($post);
            $data = [];
            foreach($post as $key=>$value){
                $data[] = $key ."=". $value;
            }
            $string = implode('&', $data);
        }

        $sign = md5($string . "&key=" . $this->versionToKey[$version]);

        $signature = (string)$this->request->header('Api-Sign', '');
        if( $signature == ''){
            $this->outPut('SIGNEMPTY', '签名参数错误');
        }


        if( $signature !== $sign){
            $this->outPut('SIGNERROR', '签名错误');
        }

        return true;
    }


    /**
     * 统一输出
     * Function outPut
     * author mselect
     * DateTime 2019-12-01
     */
    public function outPut($code, $msg='', $data = []){
        $result = array(
            'code' => $this->codeMark[$code],
            'msg' =>  $msg,
            'data' => $data,
        );
        $type                                   = $this->getResponseType();
        $response                               = Response::create($result, $type)->header($this->header);
        throw new HttpResponseException($response);
    }

    /**
     * 获取当前的response 输出类型
     * @access protected
     * @return string
     */
    protected function getResponseType()
    {
        return 'json';
    }


}
  • _checkPostData() 方法就是用来验证用户提交的数据和token 是否能通过验证的

加密方法: md5("post入参按照字母顺序递增拼接成字符串" . "不同的版本号对应的key")

前端保存有我们的版本号和该版本号对应的key; 每次前端访问接口时需要将版本号传给我们,我们根据传递的版本号得到key, 知道使用哪个key进行加密。

添加新评论