RESTFul Api Slim Framework dengan Authorization JWT - Mari Belajar Coding

Kamis, 13 Februari 2020

RESTFul Api Slim Framework dengan Authorization JWT


Slim framework merupakan microframework PHP yang banyak digunakan untuk keperluan membuat sebuah RESTful API atau web service. 

Tutorial kali ini kita akan belajar bagaimana membuat sebuah web service slim framework dengan autentikasi JSON Web Token(JWT). Json web token sendiri merupakan sebuah cara untuk mengamankan data dalam pertukaran data antara server dan client. Penjelasan secara singkatnya seperti ini, setelah pengguna berhasil login maka akan mendapatkan sebuah token. Token ini yang nantinya akan di gunakan untuk autentikasi saat pengguna melakukan request data. 

Membuat Rest Slim Framework dengan Authorization JWT

Pertama-tama kita install slim framework dengan composer. Buka cmd atau terminal, arahkan ke folder dimana teman-teman akan menginstall projek slim.  Kemudian ketikkan perintah berikut ini.

composer create-project slim/slim-skeleton SlimRestJWT

Baca juga Cara Install dan Menggunakan Composer 

Membuat Rest Slim Framework dengan Authorization JWT

Setelah berhasil install projek slim, buka directory SlimRestJWT kemudian ketikkan perintah berikut ini untuk menjalankan server PHP.

php -S localhost:8080 -t public public/index.php

Membuat Rest Slim Framework dengan Authorization JWT

Ketikkan http://localhost:8080 di browser, maka akan tampil seperti gambar dibawah ini.
Membuat Rest Slim Framework dengan Authorization JWT

Selanjutnya buat sebuah database, dalam tutorial ini kita akan menggunakan contoh database toko dengan tabel user dan produk.

CREATE DATABASE toko;
USE toko;

CREATE TABLE `produk` (
`IdProduk` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`KodeProduk` varchar(25),
`NamaProduk` varchar(255),
`HargaJual` int(11),
`Stok` int(11)
);

CREATE TABLE `user` (
`IdUser` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT ,
`Username` varchar(255),
`NamaLengkap` varchar(255),
`Email` varchar(255),
`NoHp` varchar(255),
`Password` varchar(255)
);


Setting database pada projek slim, buka src/settings.php tambahkan kode berikut ini.

// setting database toko
'db' => [
    'host' => 'localhost',
    'user' => 'root',
    'pass' => '',
    'dbname' => 'toko',
    'driver' => 'mysql'
]

Membuat Rest Slim Framework dengan Authorization JWT

Buka src/dependencies.php, tambahkan kode berikut ini.

// database toko
    $container['db'] = function ($c){
        $settings = $c->get('settings')['db'];
        $server = $settings['driver'].":host=".$settings['host'].";dbname=".$settings['dbname'];
        $conn = new PDO($server, $settings["user"], $settings["pass"]);  
        $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
        return $conn;
    };

Membuat Rest Slim Framework dengan Authorization JWT

Install JWT Authentication
Ketikkan kode berikut ini di terminal untuk mengintall library tambahan JWT Authentication. Pastikan terminal berada dalam projek SlimRestJWT.

composer require tuupola/slim-jwt-auth

Membuat Rest Slim Framework dengan Authorization JWT

Setelah library slim jwt berhasil di install, buka src/settings.php Tambahkan kode dibawah ini untuk setting jwt.

'jwt' => [
    'secret' => 'supersecretkeyyoushouldnotcommittogithub'
]

Membuat Rest Slim Framework dengan Authorization JWT

Middleware untuk validasi token.
middleware biasanya digunakan untuk memvalidasi token, api  key, mencatat log dan sebagainya. Buka src/middleware.php tambahkan middleware jwt authentication seperti dibawah ini.

<?php

use Slim\App;

return function (App $app) {
    // e.g: $app->add(new \Slim\Csrf\Guard);

    //middleware untuk validasi token JWT 
    $app->add(new Slim\Middleware\JwtAuthentication([
        "path" => "/api", /* or ["/api", "/admin"] */
        "secure" => false,
        "attribute" => "decoded_token_data",
        "secret" => "supersecretkeyyoushouldnotcommittogithub",
        "algorithm" => ["HS256"],
        "error" => function ($req, $res, $args) {
        $data["status"] = "error";
        $data["message"] = $args["message"];
        return $res
        ->withHeader("Content-Type", "application/json")
        ->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
        }
        ]));
};

Keterangan:
Parameter path diatas digunakan untuk menentukan bagian yang akan di proteksi dengan token jwt. Dalam hal ini, url yang menggunakan api/ harus menggunakan token untuk request data. Jika teman-teman tidak menentukan path maka semua url akan di proteksi dengan token jwt.

Membuat Rute Aplikasi
Rute aplikasi merupakan sebuah url yang akan di akses oleh aplikasi client. Kita akan membuat dua jenis rute, yaitu rute tanpa autentikasi JWT untuk login dan register dan juga rute menggunakan autentikasi JWT(api/) untuk request data user maupun data produk. Buka src/routes.php tambahkan firebase jwt.

use Slim\App;
use Slim\Http\Request;
use Slim\Http\Response;
use \Firebase\JWT\JWT;

Rute post login
rute ini digunakan untuk login user dengan method post dan parameter username dan password.

$app->post('/login', function (Request $request, Response $response, array $args) {
    $input = $request->getParsedBody();
    $Username=trim(strip_tags($input['Username']));
    $Password=trim(strip_tags($input['Password']));
    $sql = "SELECT IdUser, Username  FROM `user` WHERE Username=:Username AND `Password`=:Password";
    $sth = $this->db->prepare($sql);
    $sth->bindParam("Username", $Username);
    $sth->bindParam("Password", $Password);
    $sth->execute();
    $user = $sth->fetchObject();       
    if(!$user) {
        return $this->response->withJson(['status' => 'error', 'message' => 'These credentials do not match our records username.']);  
    }
    $settings = $this->get('settings');       
    $token = array(
        'IdUser' =>  $user->IdUser, 
        'Username' => $user->Username
    );
    $token = JWT::encode($token, $settings['jwt']['secret'], "HS256");
    return $this->response->withJson(['status' => 'success','data'=>$user, 'token' => $token]); 
});

Rute post register
rute ini digunakan untuk register user baru dengan method post dan parameter username, nama lengkap, email, nohp dan password.

$app->post('/register', function (Request $request, Response $response, array $args) {
    $input = $request->getParsedBody();
    $Username=trim(strip_tags($input['Username']));
    $NamaLengkap=trim(strip_tags($input['NamaLengkap']));
    $Email=trim(strip_tags($input['Email']));
    $NoHp=trim(strip_tags($input['NoHp']));
    $Password=trim(strip_tags($input['Password']));
    $sql = "INSERT INTO user(Username, NamaLengkap, Email, NoHp, Password) 
            VALUES(:Username, :NamaLengkap, :Email, :NoHp, :Password)";
    $sth = $this->db->prepare($sql);
    $sth->bindParam("Username", $Username);             
    $sth->bindParam("NamaLengkap", $NamaLengkap);            
    $sth->bindParam("Email", $Email);                
    $sth->bindParam("NoHp", $NoHp);      
    $sth->bindParam("Password", $Password); 
    $StatusInsert=$sth->execute();
    if($StatusInsert){
        $IdUser=$this->db->lastInsertId();     
        $settings = $this->get('settings'); 
        $token = array(
            'IdUser' =>  $IdUser, 
            'Username' => $Username
        );
        $token = JWT::encode($token, $settings['jwt']['secret'], "HS256");
        $dataUser=array(
            'IdUser'=> $IdUser,
            'Username'=> $Username
            );
        return $this->response->withJson(['status' => 'success','data'=>$dataUser, 'token'=>$token]); 
    } else {
        return $this->response->withJson(['status' => 'error','data'=>'error insert user.']); 
    }
});

Uji coba menggunakan Postman
rute register.
Membuat Rest Slim Framework dengan Authorization JWT

rute login
Membuat Rest Slim Framework dengan Authorization JWT
Keterangan :
setelah pengguna berhasil login, maka akan menampilkan response data user dan token. token ini yang nantinya kita gunakan sebagai autentikasi.

Buat rute dengan autentikasi JWT
karena sebelumnya kita telah menentukan autentikasi JWT berada pada path api, maka buat group api seperti dibawah ini.

$app->group('/api', function(\Slim\App $app) {
    //letak rute yang akan kita autentikasi dengan token
});

Rute get api/user/1
rute api/user ini akan menampilkan data user berdasarkan id user. rute ini berada dalam grup api dengan method get.

$app->get("/user/{IdUser}", function (Request $request, Response $response, array $args){
    $IdUser = $args["IdUser"];
    $sql = "SELECT IdUser, Username, NamaLengkap, Email, NoHp FROM `user` WHERE IdUser=:IdUser";
    $stmt = $this->db->prepare($sql);
    $stmt->bindParam("IdUser", $IdUser);
    $stmt->execute();
    $mainCount=$stmt->rowCount();
    $result = $stmt->fetchObject();
    if($mainCount==0) {
        return $this->response->withJson(['status' => 'error', 'message' => 'no result data.']); 
    }
    return $response->withJson(["status" => "success", "data" => $result], 200);
});

Untuk mengakses rute api/user ini teman-teman harus menambahkan header authorize token bearer saat request data seperti contoh digambar ini. token diperoleh saat login maupun register jika berhasil.
Membuat Rest Slim Framework dengan Authorization JWT

Jika tanpa header authorize akan menampilkan error 401 token not found.
Membuat Rest Slim Framework dengan Authorization JWT

Jika token salah akan menampilkan error 401 Signature verification failed.
Membuat Rest Slim Framework dengan Authorization JWT

Buat rute produkall untuk menampilkan semua data produk.

$app->get("/produkAll", function (Request $request, Response $response, array $args){
    $sql = "SELECT * FROM produk";
    $stmt = $this->db->prepare($sql);
    $stmt->execute();
    $mainCount=$stmt->rowCount();
    $result = $stmt->fetchAll();
    if($mainCount==0) {
        return $this->response->withJson(['status' => 'error', 'message' => 'no result data.']); 
    }
    return $response->withJson(["status" => "success", "data" => $result], 200);
});

Membuat Rest Slim Framework dengan Authorization JWT

Teman-teman bisa menambahkan rute lainnya dengan method post, get, put maupun delete.
Untuk menghindari error cors origin, tambahkan kode berikut ini sebelum rute login.

$app->options('/{routes:.+}', function ($request, $response, $args) {
    return $response;
});
$app->add(function ($req, $res, $next) {
    $response = $next($req, $res);
    return $response
        ->withHeader('Access-Control-Allow-Origin', '*')
        ->withHeader('Access-Control-Allow-Credentials', 'true')
        ->withHeader('Cache-Control', 'no-store, no-cache, must-revalidate')
        ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization, token')
        ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
});

Terakhir ubah .htaccess yang berada dalam folder public. tambahkan kode berikut ini.

RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

Kode lengkap untuk routes.php.

<?php

use Slim\App;
use Slim\Http\Request;
use Slim\Http\Response;
use \Firebase\JWT\JWT;

return function (App $app) {
    $container = $app->getContainer();

    //memperbolehkan cors origin 
    $app->options('/{routes:.+}', function ($request, $response, $args) {
        return $response;
    });
    $app->add(function ($req, $res, $next) {
        $response = $next($req, $res);
        return $response
            ->withHeader('Access-Control-Allow-Origin', '*')
            ->withHeader('Access-Control-Allow-Credentials', 'true')
            ->withHeader('Cache-Control', 'no-store, no-cache, must-revalidate')
            ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization, token')
            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    });

    $app->post('/login', function (Request $request, Response $response, array $args) {
        $input = $request->getParsedBody();
        $Username=trim(strip_tags($input['Username']));
        $Password=trim(strip_tags($input['Password']));
        $sql = "SELECT IdUser, Username  FROM `user` WHERE Username=:Username AND `Password`=:Password";
        $sth = $this->db->prepare($sql);
        $sth->bindParam("Username", $Username);
        $sth->bindParam("Password", $Password);
        $sth->execute();
        $user = $sth->fetchObject();       
        if(!$user) {
            return $this->response->withJson(['status' => 'error', 'message' => 'These credentials do not match our records username.'],200);  
        }
        $settings = $this->get('settings');       
        $token = array(
            'IdUser' =>  $user->IdUser, 
            'Username' => $user->Username
        );
        $token = JWT::encode($token, $settings['jwt']['secret'], "HS256");
        return $this->response->withJson(['status' => 'success','data'=>$user, 'token' => $token],200); 
    });

    $app->post('/register', function (Request $request, Response $response, array $args) {
        $input = $request->getParsedBody();
        $Username=trim(strip_tags($input['Username']));
        $NamaLengkap=trim(strip_tags($input['NamaLengkap']));
        $Email=trim(strip_tags($input['Email']));
        $NoHp=trim(strip_tags($input['NoHp']));
        $Password=trim(strip_tags($input['Password']));
        $sql = "INSERT INTO user(Username, NamaLengkap, Email, NoHp, Password) 
                VALUES(:Username, :NamaLengkap, :Email, :NoHp, :Password)";
        $sth = $this->db->prepare($sql);
        $sth->bindParam("Username", $Username);             
        $sth->bindParam("NamaLengkap", $NamaLengkap);            
        $sth->bindParam("Email", $Email);                
        $sth->bindParam("NoHp", $NoHp);      
        $sth->bindParam("Password", $Password); 
        $StatusInsert=$sth->execute();
        if($StatusInsert){
            $IdUser=$this->db->lastInsertId();     
            $settings = $this->get('settings'); 
            $token = array(
                'IdUser' =>  $IdUser, 
                'Username' => $Username
            );
            $token = JWT::encode($token, $settings['jwt']['secret'], "HS256");
            $dataUser=array(
                'IdUser'=> $IdUser,
                'Username'=> $Username
                );
            return $this->response->withJson(['status' => 'success','data'=>$dataUser, 'token'=>$token],200); 
        } else {
            return $this->response->withJson(['status' => 'error','data'=>'error insert user.'],200); 
        }
    });

    $app->group('/api', function(\Slim\App $app) {
        //letak rute yang akan kita autentikasi dengan token

        //ambil data user berdasarkan id user
        $app->get("/user/{IdUser}", function (Request $request, Response $response, array $args){
            $IdUser = trim(strip_tags($args["IdUser"]));
            $sql = "SELECT IdUser, Username, NamaLengkap, Email, NoHp FROM `user` WHERE IdUser=:IdUser";
            $stmt = $this->db->prepare($sql);
            $stmt->bindParam("IdUser", $IdUser);
            $stmt->execute();
            $mainCount=$stmt->rowCount();
            $result = $stmt->fetchObject();
            if($mainCount==0) {
                return $this->response->withJson(['status' => 'error', 'message' => 'no result data.'],200); 
            }
            return $response->withJson(["status" => "success", "data" => $result], 200);
        });

        //ambil semua data produk
        $app->get("/produkAll", function (Request $request, Response $response, array $args){
            $sql = "SELECT * FROM produk";
            $stmt = $this->db->prepare($sql);
            $stmt->execute();
            $mainCount=$stmt->rowCount();
            $result = $stmt->fetchAll();
            if($mainCount==0) {
                return $this->response->withJson(['status' => 'error', 'message' => 'no result data.'],200); 
            }
            return $response->withJson(["status" => "success", "data" => $result], 200);
        });

        //ambil data produk berdasarkan id produk
        $app->get("/produk/{IdProduk}", function (Request $request, Response $response, array $args){
            $IdProduk = trim(strip_tags($args["IdProduk"]));
            $sql = "SELECT * FROM produk WHERE IdProduk=:IdProduk";
            $stmt = $this->db->prepare($sql);
            $stmt->bindParam("IdProduk", $IdProduk);
            $stmt->execute();
            $mainCount=$stmt->rowCount();
            $result = $stmt->fetchObject();
            if($mainCount==0) {
                return $this->response->withJson(['status' => 'error', 'message' => 'no result data.'],200); 
            }
            return $response->withJson(["status" => "success", "data" => $result], 200);
        });

        //menyimpan data produk
        $app->post('/produk', function (Request $request, Response $response, array $args) {
            $input = $request->getParsedBody();
            $KodeProduk=trim(strip_tags($input['KodeProduk']));
            $NamaProduk=trim(strip_tags($input['NamaProduk']));
            $HargaJual=trim(strip_tags($input['HargaJual']));
            $Stok=trim(strip_tags($input['Stok']));
            $sql = "INSERT INTO produk(KodeProduk, NamaProduk, HargaJual, Stok) 
                VALUES(:KodeProduk, :NamaProduk, :HargaJual, :Stok)";
            $sth = $this->db->prepare($sql);
            $sth->bindParam("KodeProduk", $KodeProduk);             
            $sth->bindParam("NamaProduk", $NamaProduk);            
            $sth->bindParam("HargaJual", $HargaJual);                
            $sth->bindParam("Stok", $Stok);      
            $StatusInsert=$sth->execute();
            if($StatusInsert){
                return $this->response->withJson(['status' => 'success','data'=>'success insert produk.'],200); 
            } else {
                return $this->response->withJson(['status' => 'error','data'=>'error insert produk.'],200); 
            }
        });

        //update data produk berdasarkan id produk
        $app->put('/produk/{IdProduk}', function (Request $request, Response $response, array $args) {
            $input = $request->getParsedBody();
            $IdProduk = trim(strip_tags($args["IdProduk"]));

            $KodeProduk=trim(strip_tags($input['KodeProduk']));
            $NamaProduk=trim(strip_tags($input['NamaProduk']));
            $HargaJual=trim(strip_tags($input['HargaJual']));
            $Stok=trim(strip_tags($input['Stok']));
            $sql = "UPDATE produk SET KodeProduk=:KodeProduk, NamaProduk=:NamaProduk, HargaJual=:HargaJual, Stok=:Stok WHERE IdProduk=:IdProduk";
            $sth = $this->db->prepare($sql);
            $sth->bindParam("IdProduk", $IdProduk);
            $sth->bindParam("KodeProduk", $KodeProduk);              
            $sth->bindParam("NamaProduk", $NamaProduk);            
            $sth->bindParam("HargaJual", $HargaJual);                
            $sth->bindParam("Stok", $Stok);      
            $StatusUpdate=$sth->execute();
            if($StatusUpdate){
                return $this->response->withJson(['status' => 'success','data'=>'success update produk.'],200); 
            } else {
                return $this->response->withJson(['status' => 'error','data'=>'error update produk.'],200); 
            }
        });

        //delete data produk berdasarkan id produk
        $app->delete('/produk/{IdProduk}', function (Request $request, Response $response, array $args) {
            $input = $request->getParsedBody();
            $IdProduk = trim(strip_tags($args["IdProduk"]));

            $sql = "DELETE FROM produk WHERE IdProduk=:IdProduk";
            $sth = $this->db->prepare($sql);
            $sth->bindParam("IdProduk", $IdProduk);    
            $StatusDelete=$sth->execute();
            if($StatusDelete){
                return $this->response->withJson(['status' => 'success','data'=>'success delete produk.'],200); 
            } else {
                return $this->response->withJson(['status' => 'error','data'=>'error delete produk.'],200); 
            }
        });
        
    });
};

Related Posts

Load comments