Artikel ini merupakan terjemahan seri tutorial Real time Chat application built with Vue, Django, RabbitMQ and uWSGI WebSockets bagian kedua yang ditulis oleh Osaetin Daniel.
Kita akan mulai pembuatan chatire dengan melakukan implementasi manajemen user dan otentikasi sehingga user bisa membuat akun baru dan melakukan login.
Berkat komunitas Django yang luar biasa, hal-hal tadi sudah tersedia untuk kita pakai. Oleh karena itu kita akan menggunakan pustaka django pihak ketiga bernama djoser
Mari kita pasang dari
pypi
pip install djangorestframework
pip install djoser
Djoser adalah implementasi sistem
otentikasi Django berbasis REST. Jadi dengan pustaka ini kita akan
mendapatkan endpoint REST untuk melakukan registrasi user, pembuatan
token, manajemen user dll.Konfigurasi djoser
Kita mulai dengan konfigurasi palign sederhana untuk djoser. Tambahkan kode berikut diINSTALLED_APPS
INSTALLED_APPS = (
'django.contrib.auth',
...,
'rest_framework',
'rest_framework.authtoken',
'djoser',
)
lalu url djoser di urls.py
:from django.contrib import admin
from django.urls import path, include
urlpatterns = [
...,
path('auth/', include('djoser.urls')),
path('auth/', include('djoser.urls.authtoken')),
]
Masukkan rest_framework.authentication.TokenAuthentication
ke dalam kelas otentikasi django rest:REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
(...)
),
}
Terakhir, jalankan migrasi database dengan perintah python manage.py migrate
. Perintah ini akan membuat tabel-tabel yang dibutuhkan djsoer
.Hanya dengan menambah kode-kode di atas sekarang endpoint otentikasi sudah siap. Mari kita buat user baru (jalankan di terminal):
curl -X POST http://127.0.0.1:8000/auth/users/create/ --data 'username=danidee&password=mypassword'
{"email":"","username":"danidee","id":1}
Voila! Sekarang
kita sudah memiliki use baru. Lihat dokumentasi djoser untuk semua
endpoint yang tersedia dan bagaimana cara menggunakannya di http://djoser.readthedocs.io/en/latest/base_endpoints.html
Vue.js
Vue adalah Framework JavaScript untuk membuat antarmuka yang Reactive. Meskipun penulis merupakan fans berat React (karena React-native), penulis lebih memilih Vue untuk membuat aplikasi web.Salah satu alasannya ialah kurva belajar yang lebih bersahabat. Ia lebih mudah untuk mulai dipelajari dan tidak seperti React dimana kita harus bersusah payah menyiapkan banyak hal (dengan Webpack dan kawan-kawannya) untuk membuat aplikasi yang production ready. Pembaca cukup menulis tag
<script>
seperti saat akan menggunakan JQuery
.Ia juga memiliki komunitas yang giat dengan banyak plugin dan tutorial yang tersedia.
Kita akan memakai
vue-cli
untuk membuat Vue app dengan cepat (tidak menggunakan tag <script>
). Methode ini memungkinkan kita untuk menggunakan ES6+
dan single file Vue component.Pasang
vue-cli
dari npm:npm install -g vue-cli
Mari mulai proyek baru menggunakan webpack dengan
vue-cli
vue init webpack chatire-frontend
Catatan: Pastikan memilih opsi “install vue-router”
Sekarang mungkin waktu yang tepat untuk membuat secangkir kopi atau mengambil snack karena prosesnya dapat memakan waktu lama tergantung kecepatan internet kita.
Selanjutnya, masuk ke direktori aplikasi vue yang sudah muncul (diterminal dengan perintah
cd
) dan jalankan server development dengan perintah:npm run dev
.Seharusnya saat membuka alamat
localhost:8080
di browser kita akan melihat:Mari kita bahas sebentar struktur foldernya:
.
├── build
│ ├── build.js
│ ├── check-versions.js
│ ├── logo.png
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ └── webpack.prod.conf.js
├── config
│ ├── dev.env.js
│ ├── index.js
│ ├── prod.env.js
│ └── test.env.js
├── index.html
├── node_modules
├── package.json
├── package-lock.json
├── README.md
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ ├── main.js
│ └── router
│ └── index.js
├── static
└── test
├── e2e
│ ├── custom-assertions
│ │ └── elementCount.js
│ ├── nightwatch.conf.js
│ ├── runner.js
│ └── specs
│ └── test.js
└── unit
├── jest.conf.js
├── setup.js
└── specs
└── HelloWorld.spec.js
- build:
Direktori ini berisi skrips yang dipakai untuk menjalankan server
development webpack atau bundel aplikasi saat siap dipakai di
production. Contoh, perintah
npm run dev
sebetulnya memanggil perintah:
webpack-dev-server --inline --progress --config build/webpack.dev.conf.js
Opsi--inline
memasukkan file static yang di generate ke halamanindex.html
. - config: Sesuai namanya, disini tempat menyimpan file-file yang berhubungan dengan konfigurasi development, testing dan production
- src:
Disini adalah tempat menulis sebagian besar kode-kode yang kita
perlukan, ia memiliki subfolder yang yang memiliki fungsi masing-masing.
Component single file kita akan disimpan di folder ini. Akan ada satu component bawaan bernamaHelloWorld.vue
.
Fileindex.js
di folder router memiliki konfigurasi untuk vue-router. - static: File static (HTML, CSS and JavaScript) disimpan di folder ini.
- test: Terakhir, template webpack dari
vue-cli
akan mempermudah ini jika ingin melakukan pengujian aplikasi dengan membuat End to end tests (e2e) di atas Nightwatch dan unit tests yang berjalan dengan Jest.
Pengujian yang ada dapat dijalankan dengan perintahnpm run unit
(untuk unit tests) dannpm run e2e
untuk End to end tests.
vue-cli
juga menyiapkan fitur hotreloading untuk kita untuk membantu kita sehingga tidak perlu melakukan refresh di browser.Konfigurasi Vue router
Buat dua component, satu untuk halaman utama bernamaChat.vue
dan satu lagi untuk User Authentication dan Signup bernama UserAuth.vue
Idealnya apa yang kita inginkan adalah menampilkan component berdasarkan status Login user. Jika User sudah melakukan Login, maka kita ingin menampilkan component
Chat
jika tidak kita ingin menampilkan component UserAuth
.Kita dapat melakukannya dengan menggunakan global navigation. Edit file router
index.js
agar memiliki kode sebagai berikut:import Vue from 'vue'
import Router from 'vue-router'
import Chat from '@/components/Chat'
import UserAuth from '@/components/UserAuth'
Vue.use(Router)
const router = new Router({
routes: [
{
path: '/chats',
name: 'Chat',
component: Chat
},
{
path: '/auth',
name: 'UserAuth',
component: UserAuth
}
]
})
router.beforeEach((to, from, next) => {
if (sessionStorage.getItem('authToken') !== null || to.path === '/auth') {
next()
}
} else {
next('/auth')
}
})
export default router
Method beforeEach
dipanggil sebelum melakukan navigasi ke route manapun diaplikasi kita.Jika sebuah token disimpan di dalam
sessionStorage
kita akan memperbolehkan navigation melanjutkan pekerjaannya dengan memanggil next()
jika tidak kita kembali ke component auth.Ke mana pun route yang dituju oleh user di aplikasi kita, fungsi ini akan memeriksa jika user memiliki auth token dan membawanya sesuai tujuan.
Halaman Login/Signup
Penulis sudah membuat halaman Login/Signup sederhana dengan Bootstrap 4. Berikut isi kontenUserAuth.vue
:<template>
<div class="container">
<h1 class="text-center">Welcome to Chatire!</h1>
<div id="auth-container" class="row">
<div class="col-sm-4 offset-sm-4">
<ul class="nav nav-tabs nav-justified" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="signup-tab" data-toggle="tab" href="#signup" role="tab" aria-controls="signup" aria-selected="true">Sign Up</a>
</li>
<li class="nav-item">
<a class="nav-link" id="signin-tab" data-toggle="tab" href="#signin" role="tab" aria-controls="signin" aria-selected="false">Sign In</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="signup" role="tabpanel" aria-labelledby="signin-tab">
<form @submit.prevent="signUp">
<div class="form-group">
<input v-model="email" type="email" class="form-control" id="email" placeholder="Email Address" required>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<input v-model="username" type="text" class="form-control" id="username" placeholder="Username" required>
</div>
<div class="form-group col-md-6">
<input v-model="password" type="password" class="form-control" id="password" placeholder="Password" required>
</div>
</div>
<div class="form-group">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="toc" required>
<label class="form-check-label" for="gridCheck">
Accept terms and Conditions
</label>
</div>
</div>
<button type="submit" class="btn btn-block btn-primary">Sign up</button>
</form>
</div>
<div class="tab-pane fade" id="signin" role="tabpanel" aria-labelledby="signin-tab">
<form @submit.prevent="signIn">
<div class="form-group">
<input v-model="username" type="text" class="form-control" id="username" placeholder="Username" required>
</div>
<div class="form-group">
<input v-model="password" type="password" class="form-control" id="password" placeholder="Password" required>
</div>
<button type="submit" class="btn btn-block btn-primary">Sign in</button>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
const $ = window.jQuery // JQuery
export default {
data () {
return {
email: '', username: '', password: ''
}
}
}
</script>
<style scoped>
#auth-container {
margin-top: 50px;
}
.tab-content {
padding-top: 20px;
}
</style>
Pada sisipan kode di atas,
v-model
dipakai untuk melakukan two way data binding
untuk semua kolom input. Ini artinya apapun yang kita tulis di kolom
tersebut dapat langsung diakses dari JavaScript menggunakan this.field_name
.Kita juga membuat event listeners di kedua form menggunakan
@submit.prevent
yang akan mendengarkan event form submitdari setiap form dan memanggil
method yang diinginkan. Kita belum mengimplementasi method-method
tersebut.Karena kita menggunakan
Bootstrap
, daripada memanggil jQuery
dari npm
kita menggunakan variabel $
untuk mendaftarkan window.jQuery
secara global.Kita akan memakai method ajax
jQuery
untuk berkomunikasi dengan server. Silahkan gunakan pustaka ajax lain seperti Axios jika tidak ingin menggunakan jQuery
. Pustaka ini cukup populer dikalangan pengguna Vue
.Jangan lupa untuk menambahkan include file-file CSS dan JavaScript Bootstrap di halaman
index.html
.<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/css/bootstrap.min.css" integrity="sha384-Zug+QiDoJOrZ5t4lssLdxGhVrurbmBWopoEl+M6BdEfwnCJZtKxi1KgxUyJq13dy" crossorigin="anonymous">
<style>
.nav-tabs .nav-item.show .nav-link, .nav-tabs .nav-link.active {
outline: none;
}
</style>
<title>chatire-frontend</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/js/bootstrap.min.js" integrity="sha384-a5N7Y/aK3qNeh15eJKGWxsqtnX/wWdSZSKp+81YjTmS15nvnvxKHuzaWwXHDli+4" crossorigin="anonymous"></script>
</body>
</html>
Hasil yang didapat seharusnya:
Cukup oke kan?
Mari Kita Ambil auth token dari Django
Ktia ingin mendaftarkan user, memasukkannya dan membawanya ke route Chat.Untuk melakukannya, kita harus mengimplement method
signUp
dan signIn
di kode sebelumnya:methods: {
signUp () {
$.post('http://localhost:8000/auth/users/create/', this.$data, (data) => {
alert("Your account has been created. You will be signed in automatically")
this.signIn()
})
.fail((response) => {
alert(response.responseText)
})
},
signIn () {
const credentials = {username: this.username, password: this.password}
$.post('http://localhost:8000/auth/token/create/', credentials, (data) => {
sessionStorage.setItem('authToken', data.auth_token)
sessionStorage.setItem('username', this.username)
this.$router.push('/chats')
})
.fail((response) => {
alert(response.responseText)
})
}
}
Sekarang coba submit form tadi. Oops! Ada kesalahan:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8000/auth/users/create. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
CORS
Menurut situs Mozilla:Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to let a user agent gain permission to access selected resources from a server on a different origin (domain) than the site currently in use. A user agent makes a cross-origin HTTP request when it requests a resource from a different domain, protocol, or port than the one from which the current document originated.Secara sederhana CORS memperbolehkan rikues Ajax dari domain tertentu untuk melakukan
XmlHttpRequest
. Normalnya kita tidak bisa melakuakn XmlHttpRequest
dari situs dengan domain yang berbeda (catatan: jika ada yang salah harap dikoreksi).Dalam kasus ini, meskipun sama-sama berjalan di localhost, karena berjalan di port yang berbeda (8080 dan 8000) mereka dianggap berada di domain yang berbeda.
Domain yang memiliki skema (
http
atau https
), hostname (localhost
) dan port yang harus sama pula.Jadi bagaimana cara menjalankan CORS di aplikasi django? Ada pustaka pihak ketiga untuk memudahkan hal tersebut bernama
django-cors-headers
.pip install django-cors-headers
tambahkan di INSTALLED_APPS
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Custom Apps
'rest_framework',
'rest_framework.authtoken',
'corsheaders',
'djoser'
]
tambahkan juga di (Pastikan ditulis sebelum django.middleware.common.CommonMiddleware
)MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Terakhir atur CORS_ORIGIN_ALLOW_ALL = True
Catat bahwa apa yang kita lakukan mengaktifkan CORS untuk semua domain. Ini boleh dilakukan untuk development tapi saat di production kita mungkin hanya ingin domain tertentu saja
CORS_ORIGIN_WHITELIST
Baca dokumentasi django-cors-header untuk melihat opsi lain.
Setelah melakukannya, kita sekarang bisa membuat akun dan melakukan login.
Dibelakang layar,
django-cors-headers
menggunakan sebuah Middleware untuk menambah header ke setiap rikues
yang memberitahu Django bahwa rikues tersebut aman dan boleh dilakukan.Logout
Karena kita memakaisessionStorage
untuk menyimpan auth token, kita bisa memulai session baru dengan membuka tab baru.Jika ingin menyimpan token di tab baru atau saat browser di restart kita bisa memakai
localStorage
. Ia memiliki API yang sama dengan sessionStorage
sehingga kita cukup mengganti session
ke local
.Lalu kita dapat membuat sebuah fugnsi yang mengapus token dari storage dengan memanggil
removeItem
. Berikut kode untuk melakukannya dengan localStorage
.localStorage.removeItem('authToken')
Kesimpulan
Tutorial kali ini cukup sampai manajemen user dan sistem otensikasi. Kita mulai dengan memasang djoser, sebuah pustaka django yang memberikan endpointREST
untuk otentikasi.Kita juga menggunakan method ajax milik
jQuery
untuk memanggil endpoints tersebut.Selanjutnya kita membahas
Same origin policy
sedikit dan belajar bagaimana memperbolehkan rikues Ajax dari aplikasi Vue ke backend Django menggunakan CORS
lewat django-cors-headers
.Dibagian berikutnya, kita membuat model django dan API untuk aplikasi Chat.
Sumber : Membuat Aplikasi Web Realtime dengan Django, RabbitMQ dan Vue.js Bagian 2: Otentikasi dan Manajemen User
No comments:
Post a Comment