copy()->addMinutes(intval(config('jwt.ttl'))); $expiredAt = $expiredObj->toDateTimeString(); // HMAC SHA256 默认简写 HS256 $signer = new Sha256(); $key = InMemory::plainText(config('jwt.secret')); $aud = 'app-users'; $jti = md5(implode(',', [$aud, $info['user_id'], $nowObj->timestamp, random(6)])); $jwtConfig = Configuration::forSymmetricSigner($signer, $key); $tokenObj = $jwtConfig->builder() // payload-iss 签发人 ->issuedBy($this->issue) ->withHeader('iss', $this->issue) // payload-aud 受众 ->permittedFor($aud) // payload->exp 过期时间,DateTimeImmutable对象. ->expiresAt($expiredObj) // 允许在某一个时间开始就使用 ->canOnlyBeUsedAfter($nowObj->modify('-30 second')) // payload->jti 编号 ->identifiedBy($jti) // payload->iat 签发时间,DateTimeImmutable对象. ->issuedAt($nowObj) // payload 私有信息. ->withClaim('user_id', $info['user_id']) ->withClaim('version', $info['version']) ->withClaim('device_no', $info['device_no']) ->withClaim('device_type', $info['device_type']) ->withClaim('ip', $this->getClientIp()) ->getToken($jwtConfig->signer(), $jwtConfig->signingKey()); $token = $tokenObj->toString(); return [ 'token' => $token, 'expired_at' => $expiredAt, ]; } /** * 创建一个后台 Token. * @param object $manager 用户信息,包含manager_uid,role_id * @return array */ public function createManageJwt($manager) { $nowObj = CarbonImmutable::now(); $expiredObj = $nowObj->copy()->addMinutes(intval(config('jwt.mg_ttl'))); $expiredAt = $expiredObj->toDateTimeString(); // HMAC SHA256 默认简写 HS256 $signer = new Sha256(); $key = InMemory::plainText(config('jwt.secret')); $aud = 'manage-users'; $jti = md5(implode(',', [$aud, $manager->manager_uid, $nowObj->timestamp, random(6)])); $jwtConfig = Configuration::forSymmetricSigner($signer, $key); $tokenObj = $jwtConfig->builder() // payload-iss 签发人 ->issuedBy($this->issue) ->withHeader('iss', $this->issue) // payload-aud 受众 ->permittedFor($aud) // payload->exp 过期时间,DateTimeImmutable对象 ->expiresAt($expiredObj) // 允许在某一个时间开始就使用 ->canOnlyBeUsedAfter($nowObj->modify('-30 second')) // payload->jti 编号 ->identifiedBy($jti) // payload->iat 签发时间,DateTimeImmutable对象. ->issuedAt($nowObj) // payload 私有信息. ->withClaim('manager_uid', $manager->manager_uid) ->withClaim('role_id', $manager->role_id) ->withClaim('ip', $this->getClientIp()) ->getToken($jwtConfig->signer(), $jwtConfig->signingKey()); $token = $tokenObj->toString(); return [ 'token' => $token, 'expired_at' => $expiredAt, ]; } /** * 创建一个代理 Token. * @param object $agent 用户信息,包含agent_uid * @return array */ public function createAgentJwt($agent) { $nowObj = CarbonImmutable::now(); $expiredObj = $nowObj->copy()->addMinutes(intval(config('jwt.ag_ttl'))); $expiredAt = $expiredObj->toDateTimeString(); // HMAC SHA256 默认简写 HS256 $signer = new Sha256(); $key = InMemory::plainText(config('jwt.secret')); $aud = 'agent-users'; $jti = md5(implode(',', [$aud, $agent->uid, $nowObj->timestamp, random(6)])); $jwtConfig = Configuration::forSymmetricSigner($signer, $key); $tokenObj = $jwtConfig->builder() // payload-iss 签发人 ->issuedBy($this->issue) ->withHeader('iss', $this->issue) // payload-aud 受众 ->permittedFor($aud) // payload->exp 过期时间,DateTimeImmutable对象 ->expiresAt($expiredObj) // 允许在某一个时间开始就使用 ->canOnlyBeUsedAfter($nowObj->modify('-30 second')) // payload->jti 编号 ->identifiedBy($jti) // payload->iat 签发时间,DateTimeImmutable对象. ->issuedAt($nowObj) // payload 私有信息. ->withClaim('agent_uid', $agent->uid) ->withClaim('ip', $this->getClientIp()) ->getToken($jwtConfig->signer(), $jwtConfig->signingKey()); $token = $tokenObj->toString(); return [ 'token' => $token, 'expired_at' => $expiredAt, ]; } /** * 校验Token (仅适用用于HS256算法). * @param string $token * @param string $aud 受众人. * @return array */ public function validateToken(string $token, $aud = 'app-users') { // 使用的时候,只需要看is_valid,如果无效的情况下需要告知是否过期,再看is_expired $result = [ // 是否非法格式(格式错误,无法解析json). 'is_illegal' => false, // 是否有效. 'is_valid' => false, // 是否过期. 'is_expired' => false, // 解析Token对象. 'token_obj' => null, ]; $signer = new Sha256(); $key = InMemory::plainText(config('jwt.secret')); $nowObj = CarbonImmutable::now(); $jwtConfig = Configuration::forSymmetricSigner($signer, $key); $clock = new SystemClock(new DateTimeZone(config('app.timezone'))); // $jwtConfig->setValidationConstraints( // new IssuedBy($this->issue), // new SignedWith($signer, $key), // new PermittedFor($aud) // new StrictValidAt($clock) // ); try { // 传入的token可能格式无效. $tokenObj = $jwtConfig->parser()->parse($token); } catch (CannotDecodeContent $e) { $result['is_illegal'] = true; } catch (InvalidTokenStructure $e) { $result['is_illegal'] = true; } catch (UnsupportedHeaderFound $e) { $result['is_illegal'] = true; } catch (\Throwable $exception) { $result['is_illegal'] = true; } catch (\Exception $exception) { $result['is_illegal'] = true; } if ($result['is_illegal']) { return $result; } if ($jwtConfig->validator()->validate($tokenObj, new IssuedBy($this->issue), new SignedWith($signer, $key), new PermittedFor($aud))) { $result['token_obj'] = $tokenObj; } else { // ConstraintViolation 所有错误. $result['is_illegal'] = true; } if (!$result['is_illegal']) { $result['is_valid'] = $jwtConfig->validator()->validate($tokenObj, new NoExpiredValidConstraint($clock)); if ($result['is_valid']) { // 在有效的前提下,再检查详情是否过期. $result['is_expired'] = $tokenObj->isExpired($nowObj->toDateTime()); } } return $result; } /** * 获取客户端ip. * @return mixed|string */ public function getClientIp() { $ip = request()->getClientIp(); if (strpos($ip, ',')) { $temp = explode(',', $ip); $ip = $ip[0]; $temp = null; unset($temp); } return $ip; } }