Lumen中JWT token 生成过程理解,分析一下源码

我们的lumen项目中使用到了jwt,网上资料也有很多对jwt的分析,以及jwt拓展使用的

  • jwt分析
  • JWT拓展完整使用

这里记录一下jwt的token生成,以及流程,了解token的来源。

项目中的login接口中使用登录者信息调用attempt接口获取$token 追根溯源从这里开始

$credentials = ['username' =>'123', 'password' => '123456';
$token = Auth::guard('user')->attempt($credentials);

Auth::guard('user')返回了一个看守器实例。jwt拓展详解

    public static function attempt($credentials = array(), $login = true)
    {
        return \Tymon\JWTAuth\JWTGuard::attempt($credentials, $login);
    }

代码追踪找到attempt实现的是JWTGuard类下的attempt接口:对用户身份进行验证并返回令牌 验证通过调用login方法登录并返回令牌

    public function attempt(array $credentials = [], $login = true)
    {
        $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials);

        if ($this->hasValidCredentials($user, $credentials)) {
            return $login ? $this->login($user) : true;
        }

        return false;
    }

1.校验用户

$this->provider 就是 auth.php 配置文件的providers配置项

/*
    |--------------------------------------------------------------------------
    | 提供器配置
    |--------------------------------------------------------------------------
    |
    | Supported: "database", "eloquent"
    | 提供器定义怎么检索用户
    |
    */

    'providers' => [
        'admins' => [
            'driver' => 'eloquent',
            //'table'=>'t_platform_admin',
            'model' => App\Http\Models\Auth\Admin::class,
        ],
        'users' => [
            'driver' => 'eloquent',
            //'table'=>'t_admin',
            'model' => App\Http\Models\Auth\User::class,
        ],
    ],

eloquent 就是 EloquentUserProvider类,在这里我们找到了attempt调用的retrieveByCredentials方法.根据最开始给attempt方法的登录用户信息,去数据库中查找匹配

/**
     * Retrieve a user by the given credentials.
     *
     * @param  array  $credentials
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByCredentials(array $credentials)
    {
        if (empty($credentials) ||
           (count($credentials) === 1 &&
            array_key_exists('password', $credentials))) {
            return;
        }

        // First we will add each credential element to the query as a where clause.
        // Then we can execute the query and, if we found a user, return it in a
        // Eloquent User "model" that will be utilized by the Guard instances.
        $query = $this->createModel()->newQuery();

        foreach ($credentials as $key => $value) {
            if (! Str::contains($key, 'password')) {
                $query->where($key, $value);
            }
        }

        return $query->first();
    }

接下来就是用户信息的校验了,具体实现同样在 EloquentUserProvider

/**
     * Validate a user against the given credentials.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable  $user
     * @param  array  $credentials
     * @return bool
     */
    public function validateCredentials(UserContract $user, array $credentials)
    {
        $plain = $credentials['password'];

        return $this->hasher->check($plain, $user->getAuthPassword());
    }

$this->hasher->checkIlluminate\Hashing\BcryptHasher

    /**
     * Check the given plain value against a hash.
     *
     * @param  string  $value
     * @param  string  $hashedValue
     * @param  array   $options
     * @return bool
     */
    public function check($value, $hashedValue, array $options = [])
    {
        if (strlen($hashedValue) === 0) {
            return false;
        }

        return password_verify($value, $hashedValue);
    }

至此完成用户信息校验, attempt进入 $this->login($user)方法.

2.生成token

    /**
     * Create a token for a user.
     *
     * @param  \Tymon\JWTAuth\Contracts\JWTSubject  $user
     *
     * @return string
     */
    public function login(JWTSubject $user)
    {
        $token = $this->jwt->fromUser($user);
        $this->setToken($token)->setUser($user);

        return $token;
    }

$this->jwt->fromUser($user);Tymon\JWTAuth\JWT

    public function fromUser(JWTSubject $user)
    {
        return $this->fromSubject($user);
    }
    public function fromSubject(JWTSubject $subject)
    {
        $payload = $this->makePayload($subject);//生成载荷

        return $this->manager->encode($payload)->get();
    }
生成载荷Payload
    public function makePayload(JWTSubject $subject)
    {
        //生成载荷进入factory类
        return $this->factory()->customClaims($this->getClaimsArray($subject))->make();
    }

Tymon\JWTAuth\Factory

    public function customClaims(array $customClaims)
    {
        $this->customClaims = $customClaims;

        return $this;
    }
    public function make($resetClaims = false)
    {
        $payload = $this->withClaims($this->buildClaimsCollection());

        if ($resetClaims) {
            $this->emptyClaims();
        }

        return $payload;
    }
    
    public function buildClaimsCollection()
    {
        return $this->buildClaims()->resolveClaims();
    }
    
    protected function buildClaims()
    {
        // remove the exp claim if it exists and the ttl is null
        if ($this->claimFactory->getTTL() === null && $key = array_search('exp', $this->defaultClaims)) {
            unset($this->defaultClaims[$key]);
        }

        // add the default claims
        foreach ($this->defaultClaims as $claim) {
            $this->addClaim($claim, $this->claimFactory->make($claim));
        }

        // add custom claims on top, allowing them to overwrite defaults
        return $this->addClaims($this->getCustomClaims());
    }
    
    protected function resolveClaims()
    {
        return $this->claims->map(function ($value, $name) {
            return $value instanceof Claim ? $value : $this->claimFactory->get($name, $value);
        });
    }
    
    public function map(callable $callback)
    {
        $keys = array_keys($this->items);

        $items = array_map($callback, $this->items, $keys);

        return new static(array_combine($keys, $items));
    }

make() 函数返回 $payload token中载荷

生成签名

payload之后,是signer的生成, 实例调用Tymon\JWTAuth\Providers\JWT\Namshi类的encode方法

$this->manager->encode($payload)->get();
    public function encode(array $payload)
    {
        try {
            $this->jws->setPayload($payload)->sign($this->getSigningKey(), $this->getPassphrase());

            return (string) $this->jws->getTokenString();
        } catch (Exception $e) {
            throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e);
        }
    }

encode方法实现 Namshi\JOSE\JWS

    public function sign($key, $password = null)
    {
        $this->signature = $this->getSigner()->sign($this->generateSigninInput(), $key, $password);
        $this->isSigned = true;

        return $this->signature;
    }

getSigner()是通过获取到加密类型,来实例化Namshi\JOSE\Signer下指定的类.项目中使用的就是Namshi\JOSE\Signer\OpenSSL\HS256类.这个类继承了HMAC父类,而HMAC就是具体signer的实现类

    public function sign($input, $key)
    {
        return hash_hmac($this->getHashingAlgorithm(), $input, (string) $key, true);
    }

这里的($this->generateSigninInput()就是payloadheader的内容 $keyJWT_SECRET的密匙 ,$password是非对称加密下签署令牌的密码.用于生成token的第三部分signature

生成token

$this->jws->getTokenString();

    public function getTokenString()
    {
        $signinInput = $this->generateSigninInput();

        return sprintf('%s.%s', $signinInput, $this->encoder->encode($this->getSignature()));
    }
    public function generateSigninInput()
    {
        $base64payload = $this->encoder->encode(json_encode($this->getPayload(), JSON_UNESCAPED_SLASHES));
        $base64header = $this->encoder->encode(json_encode($this->getHeader(), JSON_UNESCAPED_SLASHES));

        return sprintf('%s.%s', $base64header, $base64payload);
    }

先拼接了headerpayload 再拼接了Signature返回token

Tymon\JWTAuth\Providers\JWT\Lcobucci类也实现了token的生成,不同的是通过公钥和私钥来生成 token,这里流程不再赘述,代码如下:

    public function encode(array $payload)
    {
        // Remove the signature on the builder instance first.
        $this->builder->unsign();

        try {
            foreach ($payload as $key => $value) {
                $this->builder->set($key, $value);
            }
            $this->builder->sign($this->signer, $this->getSigningKey());
        } catch (Exception $e) {
            throw new JWTException('Could not create token: '.$e->getMessage(), $e->getCode(), $e);
        }

        return (string) $this->builder->getToken();
    }

你可能感兴趣的:(lumen,jwt)