<?php
/**
 * Created by PhpStorm.
 * User: 中闽 < 1464674022@qq.com >
 * Date: 2019/12/5
 * Time: 17:44
 */

namespace app\api\controller\base;

use app\common\model\User;
use Exception;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use think\exception\HttpResponseException;

/**
 * 登入鉴权的接口父类
 * Class Permissions
 * @package app\api\controller\base
 */
class Permissions extends Base
{
    const JWT_COOKIE_NAME = 'x-token';
    const JWT_SESSION_NAME = 'user_info';

    private $userId;
    private $user;

    protected function _initialize()
    {
        parent::_initialize();
        //get token from header param body
        $jwt = $this->request->header('x-token', "");
        if (!$jwt) {
            $jwt = $this->request->param('x-token', "");
            if (!$jwt) {
                $callbackBody = file_get_contents('php://input');
                if (!$callbackBody) {
                    $this->json_error('请先登入');
                }
                $callbackJson = json_decode($callbackBody, true);
                $jwt = isset($callbackJson['x-token']) ? $callbackJson['x-token'] : '';
                if (!$jwt) {
                    $this->json_error('请先登入');
                }
            }
        }

        //check x-token
        try {
            JWT::$leeway = 60;
            $jwt_key = system_salt();
            $decoded = JWT::decode($jwt, new Key($jwt_key, 'HS256'));

            $arr = (array)$decoded;
            if (!isset($arr['exp']) || $arr['exp'] < time()) {
                self::clear_session();
                throw new \think\Exception('登入超时,请重新登录');
            } else {
                //鉴权成功
                $this->userId = $arr['data']->userId;
                $loginTime = $arr['data']->loginTime;
                $exptime = $arr['exp'];
                //限制账号多次登入
//                if ($this->getUser()->getData('login_time') > $loginTime) {
//                    $this->clear_session();
//                    throw new \think\Exception('您的账号已经在其它地方登入');
//                }
                // 每次刷新jwt,loginTime要保持不变
                self::createJwt($this->userId, $loginTime, 3600, $exptime);
                return;
            }
        } catch (Exception $e) {
            $this->json_error($this->getErrorMsg($e));
        }
    }

    private function getErrorMsg(Exception $e)
    {
        if ($e instanceof HttpResponseException) {
            return $e->getResponse()->getData()['msg'];
        }
        switch ($e->getMessage()) {
            case "Expired token":
                self::clear_session();
                $msg = '登入超时,请重新登录';
                break;
            case "Wrong number of segments":
                self::clear_session();
                $msg = 'Token验证失败,请重新登录';
                break;
            default:
                $msg = "error：" . $e->getMessage();
        }
        return $msg;
    }

    /**
     * 获取登入token,并设置登入状态
     * @param $userId int 用户id
     * @param $loginTime int 用户登入时间
     * @param int $expire 设置jwt过期时间,从当前时间开始计算
     * @param null $exp 强制指定过期时间，用来刷新jwt
     * @return string
     */
    public static function createJwt($userId, $loginTime, $expire = 3600, $exp = null)
    {
        $nowtime = time();
        $exptime = $exp ? $exp : $nowtime + $expire;
        //添加 exptime，后端可以判断过期并退出, 不通过session
        $data = ['userId' => $userId, 'loginTime' => $loginTime, 'exptime' => $exptime];
        $token = [
            'iss' => PRODUCT_URL, //签发者
            'aud' => PRODUCT_URL, //jwt所面向的用户
            'iat' => $nowtime, //签发时间
            'nbf' => $nowtime + 10, //在什么时间之后该jwt才可用
            'exp' => $exptime, //过期时间
            'data' => $data //不要存放隐私信息，jwt可以保证内容不被修改，但可以被解码查看内容
        ];
        // 得到jwt
        $jwt_key = system_salt();
        $jwt = JWT::encode($token, $jwt_key, 'HS256');

        //设置登入状态
        session(self::JWT_SESSION_NAME, $data);
        cookie(self::JWT_COOKIE_NAME, $jwt, 3600 * 12);
        return $jwt;
    }

    /**
     * 获取user对象
     * @return null|User
     */
    protected function getUser()
    {
        if (!$this->user) {
            $user = User::get($this->userId);
            if (!$user) {
                $this->json_error('找不到用户信息,请重新登入');
            }
            $this->user = $user;
        }
        return $this->user;
    }

    /**
     * 获取userid
     * @return null
     */
    protected function getUserId()
    {
        return $this->userId;
    }


    /**
     * 退出登入状态
     */
    public static function clear_session()
    {
        cookie(self::JWT_COOKIE_NAME, null);
        session(self::JWT_SESSION_NAME, null);
    }
}
