| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- <?php
- /**
- * JWT仓库.
- *
- * @author nj
- * @date 2021-01-21 22:58:08
- */
- namespace App\Repositories\Utils;
- use App\Repositories\JwtConstraints\NoExpiredValidConstraint;
- use Carbon\CarbonImmutable;
- use Lcobucci\Clock\SystemClock;
- use Lcobucci\JWT\Signer\Hmac\Sha256;
- use Lcobucci\JWT\Validation\Constraint\PermittedFor;
- use Lcobucci\JWT\ValidationData;
- use Lcobucci\JWT\Signer\Key\InMemory;
- use Lcobucci\JWT\Configuration;
- use DateTimeImmutable;
- use Lcobucci\JWT\Validation\Constraint\IssuedBy;
- use Lcobucci\JWT\Validation\Constraint\SignedWith;
- use Lcobucci\JWT\Validation\Constraint\StrictValidAt;
- use DateTimeZone;
- use Lcobucci\JWT\Encoding\CannotDecodeContent;
- use Lcobucci\JWT\Token\InvalidTokenStructure;
- use Lcobucci\JWT\Token\UnsupportedHeaderFound;
- class JwtRepository
- {
- /**
- * 签发人.
- *
- * @var string
- */
- private $issue = 'https://gfw.google.com';
- /**
- * 创建一个App Token.
- * @param array $info 用户信息,包含user_id, device_no, device_type, version
- * @return array
- */
- public function createAppJwt($info)
- {
- $nowObj = CarbonImmutable::now();
- $expiredObj = $nowObj->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;
- }
- }
|