Tutorial Token Web JSON: Contoh di Laravel dan AngularJS
Diterbitkan: 2022-03-11Dengan meningkatnya popularitas aplikasi satu halaman, aplikasi seluler, dan layanan RESTful API, cara pengembang web menulis kode back-end telah berubah secara signifikan. Dengan teknologi seperti AngularJS dan BackboneJS, kami tidak lagi menghabiskan banyak waktu untuk membuat markup, tetapi kami membangun API yang digunakan oleh aplikasi front-end kami. Back-end kami lebih tentang logika bisnis dan data, sementara logika presentasi dipindahkan secara eksklusif ke front-end atau aplikasi seluler. Perubahan ini telah menghasilkan cara baru dalam mengimplementasikan otentikasi dalam aplikasi modern.
Otentikasi adalah salah satu bagian terpenting dari aplikasi web apa pun. Selama beberapa dekade, cookie dan otentikasi berbasis server adalah solusi termudah. Namun, menangani autentikasi di Aplikasi Seluler dan Halaman Tunggal modern bisa jadi rumit, dan menuntut pendekatan yang lebih baik. Solusi paling terkenal untuk masalah otentikasi untuk API adalah OAuth 2.0 dan JSON Web Token (JWT).
Sebelum kita masuk ke tutorial JSON Web Token ini, apa sebenarnya JWT itu?
Apa itu Token Web JSON?
Token Web JSON digunakan untuk mengirim informasi yang dapat diverifikasi dan dipercaya melalui tanda tangan digital. Ini terdiri dari objek JSON yang ringkas dan aman-URL, yang ditandatangani secara kriptografis untuk memverifikasi keasliannya, dan yang juga dapat dienkripsi jika muatannya berisi informasi sensitif.
Karena strukturnya yang ringkas, JWT biasanya digunakan dalam header Authorization
HTTP atau parameter kueri URL.
Struktur Token Web JSON
JWT direpresentasikan sebagai urutan nilai yang disandikan base64url yang dipisahkan oleh karakter titik.
Berikut adalah contoh token JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 . eyJpc3MiOiJ0b3B0YWwuY29tIiwiZXhwIjoxNDI2NDIwODAwLCJodHRwOi8vdG9wdGFsLmNvbS9qd3RfY2xhaW1zL2lzX2FkbWluIjp0cnVlLCJjb21wYW55IjoiVG9wdGFsIiwiYXdlc29tZSI6dHJ1ZX0 . yRQYnWzskCZUxPwaQupWkiUzKELZ49eM7oWxAQK_ZXw
tajuk
Header berisi metadata untuk token dan minimal berisi jenis tanda tangan dan algoritma enkripsi. (Anda dapat menggunakan alat formatter JSON untuk mempercantik objek JSON.)
Contoh Header
{ "alg": "HS256", "typ": "JWT" }
Header contoh JWT ini menyatakan bahwa objek yang disandikan adalah JSON Web Token, dan ditandatangani menggunakan algoritma HMAC SHA-256.
Setelah ini dikodekan base64, kami memiliki bagian pertama dari JWT kami.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Muatan (Klaim)
Dalam konteks JWT, klaim dapat didefinisikan sebagai pernyataan tentang suatu entitas (biasanya, pengguna), serta metadata tambahan tentang token itu sendiri. Klaim tersebut berisi informasi yang ingin kami kirimkan, dan yang dapat digunakan server untuk menangani autentikasi Token Web JSON dengan benar. Ada beberapa klaim yang dapat kami berikan; ini termasuk nama klaim terdaftar, nama klaim publik dan nama klaim pribadi.
Klaim JWT Terdaftar
Ini adalah klaim yang terdaftar di registri Klaim Token Web IANA JSON. Klaim JWT ini tidak dimaksudkan untuk menjadi wajib melainkan untuk memberikan titik awal untuk serangkaian klaim yang berguna dan dapat dioperasikan.
Ini termasuk:
- iss : Penerbit token
- sub : Subjek token
- aud : Penonton token
- exp : Waktu kedaluwarsa JWT ditentukan dalam waktu Unix
- nbf : Waktu “Tidak sebelum” yang menunjukkan waktu sebelum JWT tidak boleh diterima untuk diproses
- iat : “Dikeluarkan pada” waktu, dalam waktu Unix, di mana token dikeluarkan
- jti : Klaim JWT ID memberikan pengidentifikasi unik untuk JWT
Klaim Publik
Klaim publik harus memiliki nama yang tahan benturan. Dengan membuat nama URI atau URN, penamaan tabrakan dihindari untuk JWT di mana pengirim dan penerima bukan bagian dari jaringan tertutup.
Contoh nama klaim publik dapat berupa: https://www.toptal.com/jwt_claims/is_admin
, dan praktik terbaiknya adalah menempatkan file di lokasi tersebut yang menjelaskan klaim sehingga dapat direferensikan untuk dokumentasi.
Klaim Pribadi
Nama klaim pribadi dapat digunakan di tempat-tempat di mana JWT hanya dipertukarkan dalam lingkungan tertutup antara sistem yang dikenal, seperti di dalam perusahaan. Ini adalah klaim yang dapat kami definisikan sendiri, seperti ID pengguna, peran pengguna, atau informasi lainnya.
Menggunakan nama-klaim yang mungkin memiliki makna semantik yang saling bertentangan di luar sistem tertutup atau pribadi dapat menimbulkan benturan, jadi gunakanlah dengan hati-hati.
Penting untuk dicatat bahwa kami ingin menjaga token web sekecil mungkin, jadi gunakan hanya data yang diperlukan di dalam klaim publik dan pribadi.
Contoh Payload JWT
{ "iss": "toptal.com", "exp": 1426420800, "https://www.toptal.com/jwt_claims/is_admin": true, "company": "Toptal", "awesome": true }
Contoh payload ini memiliki dua klaim terdaftar, satu klaim publik dan dua klaim pribadi. Setelah dikodekan base64, kami memiliki bagian kedua dari JWT kami.
eyJpc3MiOiJ0b3B0YWwuY29tIiwiZXhwIjoxNDI2NDIwODAwLCJodHRwOi8vdG9wdGFsLmNvbS9qd3RfY2xhaW1zL2lzX2FkbWluIjp0cnVlLCJjb21wYW55IjoiVG9wdGFsIiwiYXdlc29tZSI6dHJ1ZX0
Tanda tangan
Standar JWT mengikuti spesifikasi JSON Web Signature (JWS) untuk menghasilkan token akhir yang ditandatangani. Itu dihasilkan dengan menggabungkan Header JWT yang disandikan dan Payload JWT yang disandikan, dan menandatanganinya menggunakan algoritma enkripsi yang kuat, seperti HMAC SHA-256. Kunci rahasia tanda tangan dipegang oleh server sehingga dapat memverifikasi token yang ada dan menandatangani yang baru.
$encodedContent = base64UrlEncode(header) + "." + base64UrlEncode(payload); $signature = hashHmacSHA256($encodedContent);
Ini memberi kami bagian akhir dari JWT kami.
yRQYnWzskCZUxPwaQupWkiUzKELZ49eM7oWxAQK_ZXw
Keamanan dan Enkripsi JWT
Sangat penting untuk menggunakan TLS/SSL bersama dengan JWT, untuk mencegah serangan man-in-the-middle. Dalam kebanyakan kasus, ini akan cukup untuk mengenkripsi muatan JWT jika berisi informasi sensitif. Namun, jika kita ingin menambahkan lapisan perlindungan tambahan, kita dapat mengenkripsi payload JWT itu sendiri menggunakan spesifikasi JSON Web Encryption (JWE).
Tentu saja, jika kita ingin menghindari biaya tambahan dalam menggunakan JWE, opsi lain adalah dengan menyimpan informasi sensitif di database kita, dan menggunakan token kita untuk panggilan API tambahan ke server kapan pun kita perlu mengakses data sensitif.
Mengapa Perlu Token Web?
Sebelum kita dapat melihat semua manfaat menggunakan otentikasi JWT, kita harus melihat cara otentikasi telah dilakukan di masa lalu.
Otentikasi Berbasis Server
Karena protokol HTTP bersifat stateless, maka perlu ada mekanisme untuk menyimpan informasi pengguna dan cara untuk mengautentikasi pengguna pada setiap permintaan berikutnya setelah login. Sebagian besar situs web menggunakan cookie untuk menyimpan ID sesi pengguna.
Bagaimana itu bekerja
Browser membuat permintaan POST ke server yang berisi identifikasi pengguna dan kata sandi. Server merespons dengan cookie, yang disetel di browser pengguna, dan menyertakan ID sesi untuk mengidentifikasi pengguna.
Pada setiap permintaan berikutnya, server perlu menemukan sesi itu dan melakukan deserialize, karena data pengguna disimpan di server.
Kelemahan Otentikasi Berbasis Server
Sulit untuk diskalakan : Server perlu membuat sesi untuk pengguna dan mempertahankannya di suatu tempat di server. Hal ini dapat dilakukan dalam memori atau dalam database. Jika kita memiliki sistem terdistribusi, kita harus memastikan bahwa kita menggunakan penyimpanan sesi terpisah yang tidak digabungkan ke server aplikasi.
Cross-origin request sharing (CORS) : Saat menggunakan panggilan AJAX untuk mengambil sumber daya dari domain lain (“cross-origin”), kami dapat mengalami masalah dengan permintaan terlarang karena, secara default, permintaan HTTP tidak menyertakan cookie di lintas- permintaan asal.
Menggabungkan dengan kerangka kerja web : Saat menggunakan autentikasi berbasis server, kita terikat dengan skema autentikasi kerangka kerja kita. Sangat sulit, atau bahkan tidak mungkin, untuk berbagi data sesi antara kerangka kerja web yang berbeda yang ditulis dalam bahasa pemrograman yang berbeda.
Otentikasi Berbasis Token
Otentikasi berbasis token/JWT tidak memiliki kewarganegaraan, jadi tidak perlu menyimpan informasi pengguna dalam sesi. Ini memberi kami kemampuan untuk menskalakan aplikasi kami tanpa khawatir di mana pengguna telah masuk. Kami dapat dengan mudah menggunakan token yang sama untuk mengambil sumber daya aman dari domain selain domain tempat kami masuk.
Cara Kerja Token Web JSON
Browser atau klien seluler membuat permintaan ke server otentikasi yang berisi informasi login pengguna. Server otentikasi menghasilkan token akses JWT baru dan mengembalikannya ke klien. Pada setiap permintaan ke sumber daya terbatas, klien mengirimkan token akses di string kueri atau header Authorization
. Server kemudian memvalidasi token dan, jika valid, mengembalikan sumber daya aman ke klien.
Server otentikasi dapat menandatangani token menggunakan metode tanda tangan aman apa pun. Misalnya, algoritma kunci simetris seperti HMAC SHA-256 dapat digunakan jika ada saluran aman untuk berbagi kunci rahasia di antara semua pihak. Sebagai alternatif, sistem kunci publik asimetris, seperti RSA, dapat digunakan juga, menghilangkan kebutuhan untuk berbagi kunci lebih lanjut.
Keuntungan Otentikasi Berbasis Token
Stateless, lebih mudah untuk diskalakan : Token berisi semua informasi untuk mengidentifikasi pengguna, menghilangkan kebutuhan akan status sesi. Jika kami menggunakan penyeimbang beban, kami dapat meneruskan pengguna ke server mana pun, alih-alih terikat ke server yang sama dengan yang kami masuki.
Dapat digunakan kembali : Kami dapat memiliki banyak server terpisah, berjalan di berbagai platform dan domain, menggunakan kembali token yang sama untuk mengautentikasi pengguna. Sangat mudah untuk membangun aplikasi yang berbagi izin dengan aplikasi lain.
Keamanan JWT : Karena kami tidak menggunakan cookie, kami tidak perlu melindungi dari serangan pemalsuan permintaan lintas situs (CSRF). Kami masih harus mengenkripsi token kami menggunakan JWE jika kami harus memasukkan informasi sensitif ke dalamnya, dan mengirimkan token kami melalui HTTPS untuk mencegah serangan man-in-the-middle.
Kinerja : Tidak ada pencarian sisi server untuk menemukan dan membatalkan serial sesi pada setiap permintaan. Satu-satunya hal yang harus kita lakukan adalah menghitung HMAC SHA-256 untuk memvalidasi token dan mengurai isinya.
Contoh Token Web JSON menggunakan Laravel 5 dan AngularJS
Dalam tutorial JWT ini saya akan menunjukkan bagaimana menerapkan otentikasi dasar menggunakan Token Web JSON dalam dua teknologi web populer: Laravel 5 untuk kode backend dan AngularJS untuk contoh Single Page Application (SPA) frontend. (Anda dapat menemukan seluruh demo di sini, dan kode sumber di repositori GitHub ini sehingga Anda dapat mengikuti tutorialnya.)
Contoh token web JSON ini tidak akan menggunakan enkripsi apa pun untuk memastikan kerahasiaan informasi yang dikirimkan dalam klaim. Dalam praktiknya, ini sering kali baik-baik saja, karena TLS/SSL mengenkripsi permintaan. Namun, jika token akan berisi informasi sensitif, seperti nomor jaminan sosial pengguna, token tersebut juga harus dienkripsi menggunakan JWE.
Contoh Backend Laravel
Kami akan menggunakan Laravel untuk menangani pendaftaran pengguna, menyimpan data pengguna ke database, dan menyediakan beberapa data terbatas yang memerlukan otentikasi untuk digunakan oleh aplikasi Angular. Kami akan membuat contoh subdomain API untuk mensimulasikan Cross-Origin Resource Sharing (CORS) juga.
Instalasi dan Bootstrap Proyek
Untuk menggunakan Laravel, kita harus menginstal manajer paket Composer di mesin kita. Saat mengembangkan di Laravel, saya sarankan menggunakan "kotak" Vagrant yang sudah dikemas sebelumnya di Laravel Homestead. Ini memberi kami lingkungan pengembangan yang lengkap terlepas dari sistem operasi kami.
Cara termudah untuk bootstrap aplikasi JWT Laravel kami adalah dengan menggunakan paket Composer Laravel Installer.
composer global require "laravel/installer=~1.1"
Sekarang kita semua siap untuk membuat proyek Laravel baru dengan menjalankan laravel new jwt
.
Untuk pertanyaan apa pun tentang proses ini, silakan merujuk ke dokumentasi resmi Laravel.
Setelah kita membuat aplikasi Laravel 5 dasar, kita perlu mengatur Homestead.yaml
kita, yang akan mengonfigurasi pemetaan folder dan konfigurasi domain untuk lingkungan lokal kita.
Contoh file Homestead.yaml
:
--- ip: "192.168.10.10" memory: 2048 cpus: 1 authorize: /Users/ttkalec/.ssh/public.psk keys: - /Users/ttkalec/.ssh/private.ppk folders: - map: /coding/jwt to: /home/vagrant/coding/jwt sites: - map: jwt.dev to: /home/vagrant/coding/jwt/public - map: api.jwt.dev to: /home/vagrant/coding/jwt/public variables: - key: APP_ENV value: local
Setelah kami mem-boot kotak Vagrant kami dengan perintah vagrant up
dan masuk ke dalamnya menggunakan vagrant ssh
, kami menavigasi ke direktori proyek yang ditentukan sebelumnya. Dalam contoh di atas ini adalah /home/vagrant/coding/jwt
. Kita sekarang dapat menjalankan perintah php artisan migrate
untuk membuat tabel pengguna yang diperlukan di database kita.
Menginstal Dependensi Komposer
Untungnya, ada komunitas pengembang yang mengerjakan Laravel dan memelihara banyak paket hebat yang dapat kita gunakan kembali dan kembangkan aplikasi kita. Dalam contoh ini kita akan menggunakan tymon/jwt-auth
, oleh Sean Tymon, untuk menangani token di sisi server, dan barryvdh/laravel-cors
, oleh Barry vd. Heuvel, untuk menangani CORS.
jwt-auth
Memerlukan paket tymon/jwt-auth
di composer.json
kami dan perbarui dependensi kami.
composer require tymon/jwt-auth 0.5.*
Tambahkan JWTAuthServiceProvider
ke array penyedia app/config/app.php
.
'Tymon\JWTAuth\Providers\JWTAuthServiceProvider'
Selanjutnya, di file app/config/app.php
, di bawah array aliases
, kita tambahkan fasad JWTAuth
.
'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth'
Terakhir, kita ingin mempublikasikan konfigurasi paket menggunakan perintah berikut: php artisan config:publish tymon/jwt-auth
Token Web JSON dienkripsi menggunakan kunci rahasia. Kita dapat membuat kunci itu menggunakan perintah php artisan jwt:generate
. Itu akan ditempatkan di dalam file config/jwt.php
kami. Namun, dalam lingkungan produksi, kami tidak pernah ingin memiliki kata sandi atau kunci API di dalam file konfigurasi. Sebagai gantinya, kita harus menempatkannya di dalam variabel lingkungan server dan merujuknya di file konfigurasi dengan fungsi env
. Sebagai contoh:
'secret' => env('JWT_SECRET')
Kami dapat mengetahui lebih lanjut tentang paket ini dan semua pengaturan konfigurasinya di Github.
laravel-cors
Wajibkan paket barryvdh/laravel-cors
di composer.json
kami dan perbarui dependensi kami.
composer require barryvdh/laravel-cors 0.4.x@dev
Tambahkan CorsServiceProvider
ke array penyedia app/config/app.php
.
'Barryvdh\Cors\CorsServiceProvider'
Kemudian tambahkan middleware ke app/Http/Kernel.php
.
'Barryvdh\Cors\Middleware\HandleCors'
Publikasikan konfigurasi ke file config/cors.php
lokal dengan menggunakan perintah php artisan vendor:publish
.
Contoh konfigurasi file cors.php
:
return [ 'defaults' => [ 'supportsCredentials' => false, 'allowedOrigins' => [], 'allowedHeaders' => [], 'allowedMethods' => [], 'exposedHeaders' => [], 'maxAge' => 0, 'hosts' => [], ], 'paths' => [ 'v1/*' => [ 'allowedOrigins' => ['*'], 'allowedHeaders' => ['*'], 'allowedMethods' => ['*'], 'maxAge' => 3600, ], ], ];
Merutekan dan Menangani Permintaan HTTP

Demi singkatnya, saya akan meletakkan semua kode saya di dalam file route.php yang bertanggung jawab untuk perutean Laravel dan mendelegasikan permintaan ke pengontrol. Kami biasanya akan membuat pengontrol khusus untuk menangani semua permintaan HTTP kami dan menjaga kode kami tetap modular dan bersih.
Kami akan memuat tampilan AngularJS SPA kami menggunakan
Route::get('/', function () { return view('spa'); });
pendaftaran pengguna
Ketika kami membuat permintaan POST
ke /signup
dengan nama pengguna dan kata sandi, kami akan mencoba membuat pengguna baru dan menyimpannya ke database. Setelah pengguna dibuat, JWT dibuat dan dikembalikan melalui respons JSON.
Route::post('/signup', function () { $credentials = Input::only('email', 'password'); try { $user = User::create($credentials); } catch (Exception $e) { return Response::json(['error' => 'User already exists.'], HttpResponse::HTTP_CONFLICT); } $token = JWTAuth::fromUser($user); return Response::json(compact('token')); });
Masuk Pengguna
Saat kami membuat permintaan POST
ke /signin
dengan nama pengguna dan kata sandi, kami memverifikasi bahwa pengguna itu ada dan mengembalikan JWT melalui respons JSON.
Route::post('/signin', function () { $credentials = Input::only('email', 'password'); if ( ! $token = JWTAuth::attempt($credentials)) { return Response::json(false, HttpResponse::HTTP_UNAUTHORIZED); } return Response::json(compact('token')); });
Mengambil Sumber Daya yang Dibatasi pada Domain yang Sama
Setelah pengguna masuk, kami dapat mengambil sumber daya yang dibatasi. Saya telah membuat rute /restricted
yang mensimulasikan sumber daya yang membutuhkan pengguna yang diautentikasi. Untuk melakukan ini, header Authorization
permintaan atau string kueri perlu menyediakan JWT untuk diverifikasi oleh backend.
Route::get('/restricted', [ 'before' => 'jwt-auth', function () { $token = JWTAuth::getToken(); $user = JWTAuth::toUser($token); return Response::json([ 'data' => [ 'email' => $user->email, 'registered_at' => $user->created_at->toDateTimeString() ] ]); } ]);
Dalam contoh ini, saya menggunakan middleware jwt-auth
disediakan dalam paket jwt-auth
menggunakan 'before' => 'jwt-auth'
. Middleware ini digunakan untuk menyaring permintaan dan memvalidasi token JWT. Jika token tidak valid, tidak ada, atau kedaluwarsa, middleware akan mengeluarkan pengecualian yang dapat kita tangkap.
Di Laravel 5, kita bisa menangkap exception menggunakan file app/Exceptions/Handler.php
. Dengan menggunakan fungsi render
, kita dapat membuat respons HTTP berdasarkan pengecualian yang dilempar.
public function render($request, Exception $e) { if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException) { return response(['Token is invalid'], 401); } if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException) { return response(['Token has expired'], 401); } return parent::render($request, $e); }
Jika pengguna diautentikasi dan tokennya valid, kami dapat dengan aman mengembalikan data yang dibatasi ke frontend melalui JSON.
Mengambil Sumber Daya yang Dibatasi dari Subdomain API
Dalam contoh token web JSON berikutnya, kita akan mengambil pendekatan yang berbeda untuk validasi token. Alih-alih menggunakan middleware jwt-auth
, kami akan menangani pengecualian secara manual. Saat kami membuat permintaan POST
ke server API api.jwt.dev/v1/restricted
, kami membuat permintaan lintas asal, dan harus mengaktifkan CORS di backend. Untungnya, kita telah mengonfigurasi CORS di file config/cors.php
.
Route::group(['domain' => 'api.jwt.dev', 'prefix' => 'v1'], function () { Route::get('/restricted', function () { try { JWTAuth::parseToken()->toUser(); } catch (Exception $e) { return Response::json(['error' => $e->getMessage()], HttpResponse::HTTP_UNAUTHORIZED); } return ['data' => 'This has come from a dedicated API subdomain with restricted access.']; }); });
Contoh Frontend AngularJS
Kami menggunakan AngularJS sebagai front-end, mengandalkan panggilan API ke server otentikasi back-end Laravel untuk otentikasi pengguna dan data sampel, ditambah server API untuk data contoh lintas-asal. Setelah kita masuk ke beranda proyek kita, backend akan menyajikan tampilan resources/views/spa.blade.php
yang akan mem-bootstrap aplikasi Angular.
Berikut adalah struktur folder dari aplikasi Angular:
public/ |-- css/ `-- bootstrap.superhero.min.css |-- lib/ |-- loading-bar.css |-- loading-bar.js `-- ngStorage.js |-- partials/ |-- home.html |-- restricted.html |-- signin.html `-- signup.html `-- scripts/ |-- app.js |-- controllers.js `-- services.js
Bootstrap Aplikasi Sudut
spa.blade.php
berisi hal-hal penting yang diperlukan untuk menjalankan aplikasi. Kami akan menggunakan Twitter Bootstrap untuk penataan, bersama dengan tema khusus dari Bootswatch. Untuk mendapatkan umpan balik visual saat melakukan panggilan AJAX, kami akan menggunakan skrip bilah pemuatan sudut, yang memotong permintaan XHR dan membuat bilah pemuatan. Di bagian header, kami memiliki stylesheet berikut:
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"> <link rel="stylesheet" href="/css/bootstrap.superhero.min.css"> <link rel="stylesheet" href="/lib/loading-bar.css">
Footer markup kami berisi referensi ke perpustakaan, serta skrip khusus kami untuk modul, pengontrol, dan layanan Angular.
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script> <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script> <script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular-route.min.js"></script> <script src="/lib/ngStorage.js"></script> <script src="/lib/loading-bar.js"></script> <script src="/scripts/app.js"></script> <script src="/scripts/controllers.js"></script> <script src="/scripts/services.js"></script> </body>
Kami menggunakan perpustakaan ngStorage
untuk AngularJS, untuk menyimpan token ke penyimpanan lokal browser, sehingga kami dapat mengirimkannya pada setiap permintaan melalui header Authorization
.
Dalam lingkungan produksi, tentu saja, kami akan mengecilkan dan menggabungkan semua file skrip dan lembar gaya kami untuk meningkatkan kinerja.
Saya telah membuat bilah navigasi menggunakan Bootstrap yang akan mengubah visibilitas tautan yang sesuai, tergantung pada status masuk pengguna. Status masuk ditentukan oleh keberadaan variabel token
dalam cakupan pengontrol.
<div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">JWT Angular example</a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav navbar-right"> <li data-ng-show="token"><a ng-href="#/restricted">Restricted area</a></li> <li data-ng-hide="token"><a ng-href="#/signin">Sign in</a></li> <li data-ng-hide="token"><a ng-href="#/signup">Sign up</a></li> <li data-ng-show="token"><a ng-click="logout()">Logout</a></li> </ul> </div>
Rute
Kami memiliki file bernama app.js
yang bertanggung jawab untuk mengonfigurasi semua rute ujung depan kami.
angular.module('app', [ 'ngStorage', 'ngRoute', 'angular-loading-bar' ]) .constant('urls', { BASE: 'http://jwt.dev:8000', BASE_API: 'http://api.jwt.dev:8000/v1' }) .config(['$routeProvider', '$httpProvider', function ($routeProvider, $httpProvider) { $routeProvider. when('/', { templateUrl: 'partials/home.html', controller: 'HomeController' }). when('/signin', { templateUrl: 'partials/signin.html', controller: 'HomeController' }). when('/signup', { templateUrl: 'partials/signup.html', controller: 'HomeController' }). when('/restricted', { templateUrl: 'partials/restricted.html', controller: 'RestrictedController' }). otherwise({ redirectTo: '/' });
Di sini kita dapat melihat bahwa kita telah mendefinisikan empat rute yang ditangani oleh HomeController
atau RestrictedController
. Setiap rute sesuai dengan tampilan HTML parsial. Kami juga telah mendefinisikan dua konstanta yang berisi URL untuk permintaan HTTP kami ke backend.
Minta pencegat
Layanan $http dari AngularJS memungkinkan kita untuk berkomunikasi dengan backend dan membuat permintaan HTTP. Dalam kasus kami, kami ingin mencegat setiap permintaan HTTP dan menyuntikkannya dengan header Authorization
yang berisi JWT kami jika pengguna diautentikasi. Kami juga dapat menggunakan pencegat untuk membuat penangan kesalahan HTTP global. Berikut adalah contoh pencegat kami yang menyuntikkan token jika tersedia di penyimpanan lokal browser.
$httpProvider.interceptors.push(['$q', '$location', '$localStorage', function ($q, $location, $localStorage) { return { 'request': function (config) { config.headers = config.headers || {}; if ($localStorage.token) { config.headers.Authorization = 'Bearer ' + $localStorage.token; } return config; }, 'responseError': function (response) { if (response.status === 401 || response.status === 403) { $location.path('/signin'); } return $q.reject(response); } }; }]);
Pengendali
Dalam file controllers.js
, kita telah mendefinisikan dua pengontrol untuk aplikasi kita: HomeController
dan RestrictedController
. HomeController
menangani fungsi masuk, masuk, dan keluar. Ini meneruskan data nama pengguna dan kata sandi dari formulir masuk dan pendaftaran ke layanan Auth
, yang mengirimkan permintaan HTTP ke backend. Ini kemudian menyimpan token ke penyimpanan lokal, atau menampilkan pesan kesalahan, tergantung pada respons dari backend.
angular.module('app') .controller('HomeController', ['$rootScope', '$scope', '$location', '$localStorage', 'Auth', function ($rootScope, $scope, $location, $localStorage, Auth) { function successAuth(res) { $localStorage.token = res.token; window.location = "/"; } $scope.signin = function () { var formData = { email: $scope.email, password: $scope.password }; Auth.signin(formData, successAuth, function () { $rootScope.error = 'Invalid credentials.'; }) }; $scope.signup = function () { var formData = { email: $scope.email, password: $scope.password }; Auth.signup(formData, successAuth, function () { $rootScope.error = 'Failed to signup'; }) }; $scope.logout = function () { Auth.logout(function () { window.location = "/" }); }; $scope.token = $localStorage.token; $scope.tokenClaims = Auth.getTokenClaims(); }])
RestrictedController
berperilaku dengan cara yang sama, hanya mengambil data dengan menggunakan fungsi getRestrictedData
dan getApiData
pada layanan Data
.
.controller('RestrictedController', ['$rootScope', '$scope', 'Data', function ($rootScope, $scope, Data) { Data.getRestrictedData(function (res) { $scope.data = res.data; }, function () { $rootScope.error = 'Failed to fetch restricted content.'; }); Data.getApiData(function (res) { $scope.api = res.data; }, function () { $rootScope.error = 'Failed to fetch restricted API content.'; }); }]);
Backend bertanggung jawab untuk menyajikan data yang dibatasi hanya jika pengguna diautentikasi. Ini berarti bahwa untuk merespons dengan data yang dibatasi, permintaan untuk data tersebut harus berisi JWT yang valid di dalam header Authorization
atau string kuerinya. Jika bukan itu masalahnya, server akan merespons dengan kode status kesalahan 401 Tidak Diotorisasi.
Layanan Otentikasi
Layanan Auth bertanggung jawab untuk membuat permintaan masuk dan mendaftar HTTP ke backend. Jika permintaan berhasil, respons berisi token yang ditandatangani, yang kemudian didekodekan base64, dan informasi klaim token terlampir disimpan ke dalam variabel tokenClaims
. Ini diteruskan ke controller melalui fungsi getTokenClaims
.
angular.module('app') .factory('Auth', ['$http', '$localStorage', 'urls', function ($http, $localStorage, urls) { function urlBase64Decode(str) { var output = str.replace('-', '+').replace('_', '/'); switch (output.length % 4) { case 0: break; case 2: output += '=='; break; case 3: output += '='; break; default: throw 'Illegal base64url string!'; } return window.atob(output); } function getClaimsFromToken() { var token = $localStorage.token; var user = {}; if (typeof token !== 'undefined') { var encoded = token.split('.')[1]; user = JSON.parse(urlBase64Decode(encoded)); } return user; } var tokenClaims = getClaimsFromToken(); return { signup: function (data, success, error) { $http.post(urls.BASE + '/signup', data).success(success).error(error) }, signin: function (data, success, error) { $http.post(urls.BASE + '/signin', data).success(success).error(error) }, logout: function (success) { tokenClaims = {}; delete $localStorage.token; success(); }, getTokenClaims: function () { return tokenClaims; } }; } ]);
Servis data
Ini adalah layanan sederhana yang membuat permintaan ke server otentikasi serta server API untuk beberapa data terbatas dummy. Itu membuat permintaan, dan mendelegasikan panggilan balik sukses dan kesalahan ke pengontrol.
angular.module('app') .factory('Data', ['$http', 'urls', function ($http, urls) { return { getRestrictedData: function (success, error) { $http.get(urls.BASE + '/restricted').success(success).error(error) }, getApiData: function (success, error) { $http.get(urls.BASE_API + '/restricted').success(success).error(error) } }; } ]);
Di luar Tutorial Token Web JSON Ini
Otentikasi berbasis token memungkinkan kita untuk membangun sistem terpisah yang tidak terikat pada skema otentikasi tertentu. Token dapat dibuat di mana saja dan digunakan pada sistem apa pun yang menggunakan kunci rahasia yang sama untuk menandatangani token. Mereka siap untuk seluler, dan tidak mengharuskan kami menggunakan cookie.
Token Web JSON bekerja di semua bahasa pemrograman populer dan dengan cepat mendapatkan popularitas. Mereka didukung oleh perusahaan seperti Google, Microsoft dan Zendesk. Spesifikasi standar mereka oleh Internet Engineering Task Force (IETF) masih dalam versi draft dan mungkin sedikit berubah di masa mendatang.
Masih banyak yang harus dibahas tentang JWT, seperti cara menangani detail keamanan, dan menyegarkan token saat kedaluwarsa, tetapi tutorial JSON Web Token harus menunjukkan penggunaan dasar dan, yang lebih penting, keuntungan menggunakan JWT.
Bacaan Lebih Lanjut di Blog Teknik Toptal:
- Membangun Node.js/TypeScript REST API, Bagian 3: MongoDB, Otentikasi, dan Pengujian Otomatis