ایجاد اپلیکیشنی مدرن با استفاده از Django و Vue.js - بخش دوم

گردآوری و تالیف : ارسطو عباسی
تاریخ انتشار : 31 تیر 1397
دسته بندی ها : جاوا اسکریپت , پایتون

در قسمت قبلی بخش اولیه را اماده سازی کردیم و حالا ادامه این پیاده سازی را  انجام میدهیم .

مقدمه‌ای بر JWT

JWT  مخفف کلمات JSON Web Token است و در واقع یک JSON ساده است. می‌توانید تعریف اصلی

JWT یک URL-safe کوچک است که برای نمایش درستی یک انتقال بین دو موجودیت استفاده می‌شود. درستی در JWT در یک شئ اینکود شده داخل JSON قرار دارد و به عنوان یک payload مربوط به ساختار JSON Web Signature استفاده می‌شود. همچنین می‌شود از آن به عنوان یک متن ساده در JSON Web Encryption استفاده کرد، در این حالت درستی از طریق یک MAC ادغام و یا محافظت می‌شود. 

JWT در اصل از سه بخش تشکیل شده است: یک سربرگ، یک payload و یک امضا یا signature. قسمت سربرگ حاوی اطلاعاتی مانند الگوریتم استفاده شده برای امضا کردن payload و سربرگ است، payload نیز برای قرار گرفتن «درستی‌ها» استفاده می‌شود. برای اینکه بیشتر در این مورد اطلاعات کسب کنید می‌توانید این منبع را مطالعه نمایید. 

ایجاد یک Auth0 API

قبل از اینکه بتوانید از احرازهویت Auth0 در اپلیکیشن‌تان استفاده کنید، باید یک حساب Auth0 را ایجاد کنید. اگر چنین حسابی را ندارید می‌توانید از طریق این لینک ثبت نام کنید. 

به عنوان بخشی از فرایند ثبت نام از شما سوال می‌شود که یک دامنه Auth0  را ایجاد کنید. برای مثال django-vue-js.auth0.com می‌تواند دامنه خوبی باشد. اگر دوست دارید در این رابطه بیشتر مطالعه کنید می‌توانید به قسمت مستندات ما سر بزنید و اطلاعات خوبی را بدست بیاورید. 

بعد از آن وارد قسمت APIها شده و روی دکمه CREATE API کلیک کنید. بعد از آن با یک فرم مواجه می‌شوید که جزئیات مربوط به API را باید در آن قرار دهید.

می‌توانید برای پر کردن این فرم مانند زیر عمل کنید:

  • Name: Django Vue.js API
  • Identifier: http://djangovuejs.digituz.com.br
  • Signing Algorithm: RS256

بعد از آن روی دکمه Create کلیک کنید. شما به صفحه‌ای مراجعه داده می‌شوید که از طریق آن می‌توانید API را سفارشی‌سازی کنید. البته نیازی نیست، API شما برای ادغام سازی جانگو با Auth0 آماده است.

ادغام‌سازی جانگو با Auth0

در این قسمت شیوه ایمن‌سازی Django REST API با استفاده از Auth0 را خواهیم دید. در قسمت بعدی ما قسمت API را ایجاد می‌کنیم اما قبل از آن نیاز است که با استفاده از Auth0 احرازهویت JWT را به بک‌اند اضافه کنیم.

به این منظور شما به Django REST framework نیاز خواهید داشت. همچنین برای مدیریت احرازهویت JWT نیاز است که پکیج djangorestframework-jw را نصب کنید. شما این پکیج را برای استفاده از Auth0 در جهت امضا کردن توکن‌های JWT راه‌اندازی می‌کنید.

برای انجام اینکارها به دایرکتوری روت پروژه رفته و با استفاده از pip موارد زیر را نصب کنید:

pip install djangorestframework

pip install djangorestframework-jwt

pip install cryptography

pip install python-jose

در حین نصب کردن cryptography اگر با مشکلی مواجه شدید قبل از آن پکیج python3-dev را نصب کنید.

بعد از آن نیاز است که این پکیج‌ها را به لیست اپلیکیشن‌های نصب شده در settings.py اضافه کنید:

INSTALLED_APPS = [
    #...
    'rest_framework',
    'rest_framework_jwt'
]

همچنین نیاز است که تعریف اپلیکیشن REST_FRAMEWORK را به settings.py اضافه کنید:

REST_FRAMEWORK = {
    R 'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
       'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}

حال مطمئن شوید که نیازمندهای‌ زیر را در فایل settings.py قرار می‌دهید:

import json
from six.moves.urllib import request
from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend

در نهایت کد زیر را در پایان فایل قرار دهید:

AUTH0_DOMAIN = '<YOUR_AUTH0_DOMAIN>'
API_IDENTIFIER = '<YOUR_API_IDENTIFIER>'
PUBLIC_KEY = None
JWT_ISSUER = None

if AUTH0_DOMAIN:
    jsonurl = request.urlopen('https://' + AUTH0_DOMAIN + '/.well-known/jwks.json')
    jwks = json.loads(jsonurl.read().decode('utf-8'))
    cert = '-----BEGIN CERTIFICATE-----\n' + jwks['keys'][0]['x5c'][0] + '\n-----END CERTIFICATE-----'
    certificate = load_pem_x509_certificate(cert.encode('utf-8'), default_backend())
    PUBLIC_KEY = certificate.public_key()
    JWT_ISSUER = 'https://' + AUTH0_DOMAIN + '/'

def jwt_get_username_from_payload_handler(payload):
    return 'auth0user'

JWT_AUTH = {
    'JWT_PAYLOAD_GET_USERNAME_HANDLER': jwt_get_username_from_payload_handler,
    'JWT_PUBLIC_KEY': PUBLIC_KEY,
    'JWT_ALGORITHM': 'RS256',
    'JWT_AUDIENCE': API_IDENTIFIER,
    'JWT_ISSUER': JWT_ISSUER,
    'JWT_AUTH_HEADER_PREFIX': 'Bearer',
}

مطمئن شوید که مقدار <YOUR_AUTH0_DOMAIN> را با نام واقعی دامنه Auth0 خود و مقدار <YOUR_API_IDENTIFIER> با مقدار identifier خود در پروسه ایجاد API، جایگزین کنید. 

کلید عمومی از طریق https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json و با استفاده از متد request.urlopen() برگشت داده می‌شود. این کلید بعدا در JWT_AUTH (مربوط به djangorestframework-jwt ) به فیلد PUBLIC_KEY منتسب می‌شود. 

JWT_ISSUER نام دامنه Auth0 شما است که همراه با پیشوند https می‌آید.

JWT_ALGORITHM باید برابر مقدار RS256 قرار بگیرد. برهمین اساس باید مطمئن شوید که API مربوط به Auth0 نیز از الگوریتم RS256 بهره می‌برد.

مقدار JWT_AUTH_HEADER_PREFIX برابر با Bearer قرار گرفته است. این مقدار پیشفرضی است که در بیشتر فریمورک‌ها به عنوان پیشوند استفاده می‌شود.

شما همچنین نیاز دارید که JWT_PAYLOAD_GET_USERNAME_HANDLER را به عنوان یک متد سفارشی تنظیم کنید. 

حال نیاز است که داده‌های مربوط به کاربران را از طریق یک تابع فراخوانی کرده و آن را در اختیار Auth0 قرار دهیم. برای اینکار نیاز است به صورت زیر عمل کنیم:

python manage.py makemigrations --empty catalog

این ک برای ما یک فایل را در catalog/migrations/0001_initial.py ایجاد می‌کند. در این فایل می‌توانید محتوا را با کدهای زیر جایگزین کنید:

from django.db import migrations
from django.conf import settings

def create_data(apps, schema_editor):
    User = apps.get_model(settings.AUTH_USER_MODEL)
    user = User(pk=1, username="auth0user", is_active=True , email="admin@techiediaries.com")
    user.save()

class Migration(migrations.Migration):
    dependencies = [
    ]
    operations = [
        migrations.RunPython(create_data),
    ]

برای اینکه اطلاعات بیشتری راجع به این موضوع پیدا کنید می‌توانید این مستندات را مطالعه نمایید.

بعد از آن تنها کافی‌ست که migrate را برای ایجاد داده‌های اولیه اجرا کنید:

python manage.py migrate

متد سفارشی که JWT payload را با کاربر auth0user پایش می‌کند:

def jwt_get_username_from_payload_handler(payload):
    return 'auth0user'

اضافه کردن Viewهای جانگو

برای اینکه مطمئن شوید که JWT با Auth0 کار می‌کند می‌توانید دو تابع view را در catalog/views.py ایجاد کنید:

from rest_framework.decorators import api_view
from django.http import HttpResponse


def public(request):
    return HttpResponse("You don't need to be authenticated to see this")


@api_view(['GET'])
def private(request):
    return HttpResponse("You should not see this message if not authenticated!")

همچنین مطمئن شوید که URL مربوط را در urls.py ایجاد می‌کنید. می‌توانید کل فایل را با محتوای زیر جایگزین کنید:

from django.conf.urls import url
from catalog import views


urlpatterns = [
    url(r'^api/public/', views.public),
    url(r'^api/private/', views.private)
]

اگر پروژه جانگو را اجرا کنید، قابلیت دسترسی به http://127.0.0.1:8000/api/public/ را خواهید داشت. اما /api/private نیاز به توکن برای دسترسی خواهد داشت.

فعال‌سازی CORS روی جانگو

همانطور که شما مشغول ایجاد یک پروژه ویوجی‌اس همراه با جانگو هستید مطمئنا نیاز دارید که CORS را روی پروژه جانگو نیز نصب کنید. برای انجام این کار می‌توانید django-cors-headers را به صورت زیر نصب کنید:

pip install django-cors-headers

بعد از آن نیاز است که آن را به اپلیکیشن‌های نصب شده در فایل settings.py اضافه کنید. 

INSTALLED_APPS = [
    # ...
    'corsheaders',
]

بعد از آن باید CorsMiddleware را نیز به settings.py اضافه کنید:

MIDDLEWARE = [  # Or MIDDLEWARE_CLASSES on Django < 1.10
    # ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
]

آخرین چیزی که نیاز به پیکربندی دارد، منابعی است که در اپلیکیشن جانگو قبول می‌شود:

CORS_ORIGIN_WHITELIST = (
    'localhost:8080',
)

می‌توانید این پیکربندی را به عنوان آخرین قسمت به فایل settings.py اضافه کنید.

ادغام‌سازی Auth0 با Vue.js

در این بخش قصد داریم شما را با روشی برای ادغام‌سازی ویوجی‌اس با Auth0 آشنا کنیم.

ایجاد یک Auth0 Client

برای نمایش اپلیکیشن Vue.js روی Auth0 نیاز دارید که یک Auth0 Client جدید را ایجاد کنید. برای انجام چنین کاری به قسمت داشبورد Auth0 رفته و روی دکمه New Client کلیک کنید.

بعد از آن با برگه‌ای مواجه می‌شود که از شما نام Client و نوع آن را می‌خواهد. می‌توانید نام آن را Django Vue.js APP بگذارید و نوع آن را Single Page Web Application انتخاب کنید.

بعد از پر کردن فرم، روی دکمه Create کلیک کنید. بعد از اینکار به برگه‌ای منتقل می‌شود که در آن تبی با عنوان Settings وجود دارد. در داخل آن فیلدی با عنوان Allowed Callback URLs را پیدا کنید. در آن فیلد مقدار http://localhost:8080/ را قرار داده و روی دکمه Save کلیک کنید.

ایجاد سرویس احرازهویت

بعد از انجام مراحل بالا حال می‌توانید روی ادغام‌سازی Auth0 و اپلیکیشن Vue.js تمرکز کنید. برای انجام چنین کاری کتابخانه‌های auth0.js و eventemitter3  را نصب کنید:

# change directory to frontend
cd frontend

# install dependencies
npm install --save auth0-js
npm install --save eventemitter3

همچنین برای ارسال درخواست‌های AJAX به بک‌اند جانگو نیاز به نصب Axois دارید:

npm install --save axios

بعد از نصب این سه نیازمندی یک فایل جدید را در ./frontend/src/auth/ ایجاد کنید و آن را AuthService.js بنامید. کدهای زیر را در آن قرار دهید:

import auth0 from 'auth0-js'
import EventEmitter from 'eventemitter3'
import router from './../router'

export default class AuthService {
  authenticated = this.isAuthenticated();
  authNotifier = new EventEmitter();

  constructor () {
    this.login = this.login.bind(this)
    this.setSession = this.setSession.bind(this)
    this.logout = this.logout.bind(this)
    this.isAuthenticated = this.isAuthenticated.bind(this)
    this.handleAuthentication = this.handleAuthentication.bind(this)
  }

  // create an instance of auth0.WebAuth with your
  // API and Client credentials
  auth0 = new auth0.WebAuth({
    domain: '<YOUR_AUTH0_DOMAIN>',
    clientID: '<YOUR_CLIENT_ID>',
    redirectUri: '<YOUR_CALLBACK_URL>',
    audience: '<YOUR_AUDIENCE>',
    responseType: 'token id_token',
    scope: 'openid profile'
  });

  // this method calls the authorize() method
  // which triggers the Auth0 login page
  login () {
    this.auth0.authorize()
  }

  // this method calls the parseHash() method of Auth0
  // to get authentication information from the callback URL
  handleAuthentication () {
    this.auth0.parseHash((err, authResult) => {
      if (authResult && authResult.accessToken && authResult.idToken) {
        this.setSession(authResult)
      } else if (err) {
        console.log(err)
        alert(`Error: ${err.error}. Check the console for further details.`)
      }
      router.replace('/')
    })
  }

  // stores the user's access_token, id_token, and a time at
  // which the access_token will expire in the local storage
  setSession (authResult) {
    // Set the time that the access token will expire at
    let expiresAt = JSON.stringify(
      authResult.expiresIn * 1000 + new Date().getTime()
    )
    localStorage.setItem('access_token', authResult.accessToken)
    localStorage.setItem('id_token', authResult.idToken)
    localStorage.setItem('expires_at', expiresAt)
    this.authNotifier.emit('authChange', {authenticated: true})
  }

  // remove the access and ID tokens from the
  // local storage and emits the authChange event
  logout () {
    localStorage.removeItem('access_token')
    localStorage.removeItem('id_token')
    localStorage.removeItem('expires_at')
    this.authNotifier.emit('authChange', false)
    // navigate to the home route
    router.replace('/')
  }

  // checks if the user is authenticated
  isAuthenticated () {
    // Check whether the current time is past the
    // access token's expiry time
    let expiresAt = JSON.parse(localStorage.getItem('expires_at'))
    return new Date().getTime() < expiresAt
  }

  // a static method to get the access token
  static getAuthToken () {
    return localStorage.getItem('access_token')
  }

  // a method to get the User profile
  getUserProfile (cb) {
    const accessToken = localStorage.getItem('access_token')
    if (accessToken) return this.auth0.client.userInfo(accessToken, cb)
    else return null
  }
}

در کدهای بالا نیاز است که مقادیر مربوط به <YOUR_AUTH0_DOMAIN>، <YOUR_CLIENT_ID>، <YOUR_CALLBACK_URL> و <YOUR_AUDIENCE> را با مقادیر مربوطه در قسمت تنظیمات Client و API جایگزین کنید.

ایجاد صفحه اصلی

حال شما باید صفحه خانگی Vue.js را برای اجازه دادن به کاربران در جهت احرازهویت از طریق Auth0 و دریافت پیام‌های خصوصی و عمومی، بازسازی کنید. برای اینکار فایل ./frontend/src/App.vue را باز کرده و قسمت <template> را با کدهای زیر جایگزین کنید:

<template>
  <div>
    <button
      class="btn btn-primary btn-margin"
      v-if="!authenticated"
      @click="login()">
      Log In
    </button>

    <button
      class="btn btn-primary btn-margin"
      v-if="authenticated"
      @click="privateMessage()">
      Call Private
    </button>

    <button
      class="btn btn-primary btn-margin"
      v-if="authenticated"
      @click="logout()">
      Log Out
    </button>
    {{message}}
    <br>
  </div>
</template>

ایجاد متدها

در همان فایل قبلی قسمت <script> را نیز با کدهای زیر جایگزین کنید:

<script>
import AuthService from './auth/AuthService'
import axios from 'axios'

const API_URL = 'http://localhost:8000'
</script>

این کدها AuthService را از ./auth/AuthService و کتابخانه axios صادر می‌کند. بعد از آن مقدار ثابت API_URL را نیز نگه‌داری می‌کند.

بعد از مقدار ثابت API_URL یک نمونه از AuthService را ایجاد کنید:

const auth = new AuthService()

بعد از آن در پایین تعریف auth شروع به ساخت کامپوننت اپلیکیشن کنید:

export default {
  name: 'app',
  data () {
    this.handleAuthentication()
    this.authenticated = false

    auth.authNotifier.on('authChange', authState => {
      this.authenticated = authState.authenticated
    })

    return {
      authenticated: false,
      message: ''
    }
  },
}

در این کدها شما به رویداد authChange که از AuthService در زمان تغییر وضعیت احرازهویت ارائه می‌شود گوش می‌دهید. بعد از آن نتیجه را به متغیر authenticated می‌فرستید. همچنین در پایان مقدار متغیر message را خالی می‌گذارید.

آخرین چیزی که باید انجام دهید تعریف خاصیت methods در کامپوننت app است:

export default {
  // name: 'app',
  // data () { ... },
  methods: {
    // this method calls the AuthService login() method
    login () {
      auth.login()
    },
    handleAuthentication () {
      auth.handleAuthentication()
    },
    logout () {
      auth.logout()
    },
    privateMessage () {
      const url = `${API_URL}/api/private/`
      return axios.get(url, {headers: {Authorization: `Bearer ${AuthService.getAuthToken()}`}}).then((response) => {
        console.log(response.data)
        this.message = response.data || ''
      })
    }
  }
}

تست ادغام‌سازی Django, Vue.js و Auth0

حال که همه چیز به درستی پیکربندی شده است، می‌توانید ادغام‌سازی مربوط به این سه مورد را تست کنید. برای اینکار به دایرکتوری ریشه برگشته و دستورات زیر را اجرا کنید:

# run Django in the background
python manage.py runserver &

# move to the frontend directory
cd frontend

# run Vue.js in the background &
npm run dev &

بعد از اجرای پروژه فرانت‌اند و بک‌اند می‌توانید به صفحه vue.js در آدرس http://localhost:8080 مراجعه کنید و آن را تست نمایید. 

در صفحه اصلی شما یک دکمه Log in را مشاهده می‌کنید، روی آن کلیک کرده تا به صفحه ورود Auth0 ریدایرکت شوید. 

بعد از احرازهویت به اپلیکیشن Vue.js برگشت داده خواهید شد. 

نتیجه‌گیری و مرحله بعدی

 در این مقاله پروژه جانگو و یک پروژه فرانت‌اند ویوجی‌اس را ایجاد کردیم. همچنین مراحل احرازهویت JWT را به بک‌اند اضافه نمودیم. می‌توانید با مطالعه لینک‌هایی که در این مقاله ارائه شدند در رابطه با تمام مراحل با آشنایی کامل‌تر مطالعه کنید.

منبع

مقالات پیشنهادی

ایجاد اپلیکیشنی مدرن با استفاده از Django و Vue.js - بخش اول

در این مقاله ما قصد داریم از چهارچوب Django و Django REST برای بک‌اند و Vue.js برای فرانت‌اند است. APIها با کمک Axois (یک کتابخانه HTTP Client) از طری...

وب سایت های الهام بخش برای طراحی - سری 8

امروز قصد داریم یک سری وبسایت های خارجی که بطور کاربردی ، زیبا و قدرتمند طراحی شدن رو براتون قرار بدیم تا شما بتونین با طریقه طراحی اونها آشنا بشین یا...

25 وبسایت الهام بخش با استفاده از پارالاکس

parallax scrolling هنوز در کشور ما بخوبی جا نیوفتاده و مخاطب های خودشو به طور کامل بدست نیاورده اما در وب سایت های غیر ایرانی به وفور از این تکنیک است...

وب سایت های الهام بخش برای طراحی - سری ششم

امروز قصد داریم یک سری وبسایت های خارجی که بطور کاربردی ، زیبا و قدرتمند طراحی شدن رو براتون قرار بدیم تا شما بتونین با طریقه طراحی اونها آشنا بشین یا...