PHP Laravel & Lumen JWT (JSON Web Token) Kullanımı

JBM Yazılım Geliştirme

Yeni bir Laravel Projesi İndiriyoruz

composer create-project --prefer-dist laravel/laravel laravel-jwt

 

JWT işlemleri Firebase'e ait php-jwt kütüphanesini indiriyoruz

composer require firebase/php-jwt

 

JWT

JWT nedir bilmeyenler için, RFC 7519 standardına dayalı bir token sistemidir ve 3 parçadan oluşmaktadır.

JWT'nin asıl amacı, sunucu bağımsız bir şekilde iki ayrı platformun (örneğin web-mobil) birbirleri ile güvenli bir şekilde iletişim kurabilmesini sağlar. Daha detaylı bilgiye https://jwt.io/ üzerinden erişebilirsiniz.

Tokenlar okunabilir verilerdir bu nedenle asla kritik bilgilerin (kredi kartı) saklanmaması gerekmektedir!

Peki okunabilirse nasıl güvenli olabiliyor?

Veriler okunabilir, ancak değiştirilemezdir. Bir token oluşturulurken belirlediğimiz gizli bir anahtar bulunmaktadır. Bu anahtar sayesinde, anahtara erişimi olmayan hiçbir kullanıcı token içerisindeki veriyi değiştiremez.

Örnek bir JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

 

Token Oluşturmak

Öncelikle composer ile indirdiğimiz firebase'e ait olan php-jwt kütüphanesi dahil etmemiz gerekmektedir.

Birinci parametre olarak token içerisinde tutulacak veriler girilir.

iat (issued at) token'ın ne zaman oluşturulduğu tarih,

exp (expiration) ise token'ın zamanının ne zaman biteceğini belirttiğimiz kısım, timestamp olarak verilen bir alandır.

İkinci parametre ise az önce bahsettiğim gizli anahtar. Bunu .env dosyasından veya config dosyasından çekerek girebilir ya da direkt olarak string biçimde yazabilirsiniz.

use \Firebase\JWT\JWT;
$jwt = JWT::encode([
        'name' => 'Codethereal'
        'email' => 'dogukan@jbm.com.tr',
        'iat' => time(),
        "exp" => time() + 60 * 60 * 4 # 4 saatlik bir jwt oluşturuyoruz.
], env('JWT_SECRET'));

 

Laravel Artisan yardımı ile middleware oluşturuyoruz

php artisan make:middleware JwtVerify

Yukarıdaki komutu yazdıktan sonra app/Http/Middleware altında JwtVerify.php isimli bir dosya bulacaksınız.

<?php

namespace App\Http\Middleware;

use Closure;

use \Firebase\JWT\JWT;

class JwtVerify
{

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    { }
}

use \Firebase\JWT\JWT; ile kütüphaneyi dahil ettik.

Laravel Middleware

Laravel middleware içerisinde handle fonksiyonunda gördüğünüz gibi iki adet parametremiz bulunmaktadır. Bunlardan birisi request diğeri ise Closure sınıfından türemiş bir next parametresi. $request objesinden gelen istekteki verileri, header bilgilerini vs gibi verileri okuyabilirsiniz.  $next ise size middleware içerisinde herhangi bir problem oluşmadığında yazılımın devam edebilmesi için gerekli işlemi sağlar.

Laravel middleware çağırıldığında handle metodu çalışır yani işlemlerimizi bu metod içerisinde gerçekleştireceğiz. 

Öncelikle gelen verilerideki token'i almamız gerekmektedir. Bu sizin token'ı nasıl yolladığınıza bağlı olarak değişir ancak aşağıda belirtilen kod içerisinde $request objesinden erişebildiğimiz Authorization header'ı içerisinde yollanmıştır.

Tokenlar genelde Bearer {{ TOKEN }} şeklinde yollanır. Bu sebeple aldığımız header'ı explode fonksiyonu boşluk karakterinden ayırıyoruz ve bize bir dizi döndürüyor.

$authorizationHeader = explode(' ',$request->header('Authorization')); // ['Bearer', 'TOKEN']
$head = isset($authorizationHeader[0]) ? $authorizationHeader[0]: false;
$jwt = isset($authorizationHeader[1]) ? $authorizationHeader[1]: false;

En başta öncelikle $head değişkeni veya $jwt değişkenimiz bulunmuyorsa "Geçersiz kullanıcı" değeri döndürüyoruz. Yapmanıza gerek yok, çünkü zaten token'ı doğrulamayı denediğimiz zaman hata çıkaracaktır.

if(!$head || !$jwt){
        return response()->json([
            'status' => 0,
            'reply' => 'Geçersiz kullanıcı!'
       ]);
 }

 

Token Doğrulama

try{
      $secretKey = env('JWT_SECRET');
      $decoded = JWT::decode($jwt, $secretKey, array('HS256'));

      $request->attributes->add(['decoded' => $decoded, 'jwt' => $jwt]);
       return $next($request);
} catch (ExpiredException $e) {
        return response()->json([
             'status' => 0,
             'reply' => 'Süresi dolmuş token!'
        ], 400);
} catch (\Exception $e) {
        return response()->json([
            'status' => 0,
            'reply' => 'Geçersiz Kullanıcı!'
        ], 400);
}

Yukarıdaki kod içerisinde bir $secretKey belirlememiz gerekmektedir, bunu laravel içerisinde bulunan .env dosyası içerisinde saklayabilir veya elle verebilirsiniz. Bunu bir şifre gibi düşünebilirsiniz.

Daha sonra JWT::decode() fonksiyonu ile decode ediyoruz, eğer decode ederken herhangi bir hata oluştuysa otomatik olarak catch bloğuna düşecektir.

Daha sonra decode etme işlemi başarılı ise $request objesine jwt içerisinde bulunan çözülmüş veriyi ve token'ı veriyoruz. Son olarak da middleware içerisinde işlemler başarıyla tamamlandıysa bir $next($request) ile sonraki işlemi çağırıyoruz (bu başka bir middleware olabilir veya controller fonksiyonuna gidebilir, sizin route yapınıza bağlı.)

Eğer süresi dolmuş bir token ise ilk catch bloğuna düşecektir diğer tüm hatalar (jwt hatalıdır, doğru fomatta değildir vs) için ise ikinci catch bloğuna düşecektir.

 

Tamamı:

<?php

namespace App\Http\Middleware;

use Closure;

use \Firebase\JWT\JWT;

class JwtMiddleware
{

    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $authorizationHeader = explode(' ',$request->header('Authorization'));
        $head = isset($authorizationHeader[0]) ? $authorizationHeader[0]: false;
        $jwt = isset($authorizationHeader[1]) ? $authorizationHeader[1]: false;

        if(!$head || !$jwt){
            return response()->json([
                'status' => 0,
                'reply' => 'Geçersiz kullanıcı!'
            ]);
        }
        try{
            $secretKey = env('JWT_SECRET');
            $decoded = JWT::decode($jwt, $secretKey, array('HS256'));

            $request->attributes->add(['decoded' => $decoded, 'jwt' => $jwt]);
            return $next($request);
        } catch (ExpiredException $e) {
            return response()->json([
                'status' => 0,
                'reply' => 'Süresi dolmuş token!'
            ], 400);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 0,
                'reply' => 'Geçersiz Kullanıcı!'
            ], 400);
        }
    }
}

Daha sonra yazdığımız middleware'i kullanmak için app\Http altında bulunan Kernel.php dosyasına dahil etmemiz gerekiyor. $routeMiddleware isimli dizi değişkenine aşağıdaki kodu eklememiz gerekiyor.

'jwt' => \App\Http\Middleware\JwtVerify::class,

Artık istediğimiz route'a veya route grubuna ekleyebilirsiniz.

Route::get('/', "MyController@index")->middleware('jwt');

İsterseniz app\Providers altında bulunan RouteServiceProvider.php isimli dosya içerisinde de middleware ekleyebilirsiniz. Örneğin /api isimli route'a ekleyelim.

protected function mapApiRoutes()
    {
        Route::prefix('api')
            ->middleware(['api', 'jwt'])
            ->namespace($this->namespace)
            ->group(base_path('routes/api.php'));
    }

 

Örnek İstek

const get = async (url) => {
  const response = await fetch(url, {
    method: 'GET',
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer {{token}}"
    }
  })
  return await response.json()
};

0 Yorum

İlk yorumu sen ekle!