* 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('请先登入', null, self::ERR_CODE_LOGIN); } $callbackJson = json_decode($callbackBody, true); $jwt = isset($callbackJson['x-token']) ? $callbackJson['x-token'] : ''; if (!$jwt) { $this->json_error('请先登入', null, self::ERR_CODE_LOGIN); } } } //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) { if ($e instanceof HttpResponseException) { $this->json_error($e->getResponse()->getData()['msg']); } switch ($e->getMessage()) { case "Expired token": self::clear_session(); $this->json_error('登入超时,请重新登录', null, self::ERR_CODE_LOGIN); break; case "Wrong number of segments": self::clear_session(); $this->json_error('Token验证失败,请重新登录', null, self::ERR_CODE_LOGIN); break; default: $msg = "error:" . $e->getMessage(); $this->json_error($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('找不到用户信息,请重新登入', null, self::ERR_CODE_LOGIN); } $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); } }