Django+JWT驗證(上)
JSON Web Token (JWT) 定義了一種簡單穩定的方式來表示身份驗證和授權數據。本篇使用 Django REST Framework 搭配 Simple JWT 實作。
實作切分8步驟 (本篇包含1~5)
- 前置作業:新增Django專案
- 加入Notes應用
- 加入JWT應用
- 加上index.html與404.html頁面
- 加上JS檔案
- 加入Swagger文檔
- 加入permission
- 撰寫 python client 端檔案 call api
1. 前置作業:新增Django專案
$ django-admin startproject xsetting
$ cd xsetting
$ python manage.py makemigrations
$ python manage.py migrate
建立 superuser (帳號admin/密碼admin1234)
$ python manage.py superuser
輸入欲設定的帳號密碼
執行專案
$ python manage.py runserver
此時,訪問 http://127.0.0.1:8000/admin/
登入帳號密碼查看後台
瀏覽器輸入 http://127.0.0.1:8000/ 開啟可以看到火箭
2. 加入Notes應用
參考文章:https://learnku.com/python/t/36327
在 xsetting/settings.py 檔案中的 INSTALLED_APPS中加入
'rest_framework',
'corsheaders',
'notes',
並在其中加上
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.AllowAny',],
'DEFAULT_PARSER_CLASSES':['rest_framework.parsers.JSONParser',],
}
由於新增 INSTALLED_APPS,終端機輸入:
$ python manage.py migrate
notes/models.py新增內容
from django.db import models
from django.contrib.auth.models import User
class Note(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
content = models.TextField()
def __str__(self):
return self.title
notes/admin.py新增內容
from django.contrib import admin
from .models import Note
admin.site.register(Note)
修改 xsetting/urls.py 文件
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('notes.urls'))
]
notes目錄下新增urls.py
from django.urls import path
from rest_framework.routers import SimpleRouter
from .views import NoteViewSet
router = SimpleRouter()
router.register('notes', NoteViewSet, basename='notes')
urlpatterns = router.urls
notes目錄下新增serializers.py
from rest_framework import serializers
from .models import Note
class NoteSerializer(serializers.ModelSerializer):
class Meta:
fields = ('id', 'owner', 'title', 'content')
model = Note
notes/views.py 中創建視圖
from rest_framework import viewsets
from .models import Note
from .serializers import NoteSerializer
class NoteViewSet(viewsets.ModelViewSet):
queryset = Note.objects.all()
serializer_class = NoteSerializer
登入帳號密碼,點選Notes做建立,owner選擇admin。Title輸入’test01',Content輸入’test01'。建立成功時,owner的值為1。
後台登入的情況下 可以查看操作
http://127.0.0.1:8000/api/notes/
3. 加入JWT應用
參考 Simple JWT 的官網
https://django-rest-framework-simplejwt.readthedocs.io/en/latest/getting_started.html
先準備好以下:
- Python (3.7, 3.8, 3.9, 3.10)
- Django (2.2, 3.1, 3.2, 4.0)
- Django REST Framework (3.10, 3.11, 3.12, 3.13)
使用JWT library
$ pip install djangorestframework_simplejwt
創建一個新的應用
$ python manage.py startapp jwtauth
接著在 xsetting/settings.py 文件中加上
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated',],
'DEFAULT_PARSER_CLASSES': ['rest_framework.parsers.JSONParser',],
'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication','rest_framework_simplejwt.authentication.JWTAuthentication',],
}
同個檔案新增 jwtauth 至 INSTALLED_APPS
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'corsheaders',
'notes',
'jwtauth',
]
由於新增 INSTALLED_APPS,終端機輸入:
$ python manage.py migrate
在 jwtauth 中,建立serializers.py檔案
from django.contrib.auth import get_user_model
from rest_framework import serializers
User = get_user_model()
class UserCreateSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=True, style={'input_type':'password'})
password2 = serializers.CharField(style={'input_type': 'password'}, write_only=True, label='Confirm password')
class Meta:
model = User
fields = [
'username',
'email',
'password',
'password2',
]
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
username = validated_data['username']
email = validated_data['email']
password = validated_data['password']
password2 = validated_data['password2']
if (email and User.objects.filter(email=email).exclude(username=username).exists()):
raise serializers.ValidationError(
{'email': 'Email addresses must be unique.'})
if password != password2:
raise serializers.ValidationError(
{'password': 'The two passwords differ.'})
user = User(username=username, email=email)
user.set_password(password)
user.save()
return user
把視圖加到jwtauth/views.py
from django.contrib.auth import get_user_model
from rest_framework import permissions
from rest_framework import response, decorators, permissions, status
from rest_framework_simplejwt.tokens import RefreshToken
from .serializers import UserCreateSerializer
User = get_user_model()
@decorators.api_view(['POST'])
@decorators.permission_classes([permissions.IsAuthenticated])
def registration(request):
serializer = UserCreateSerializer(data=request.data)
if not serializer.is_valid():
return response.Response(serializer.errors, status.HTTP_400_BAD_REQUEST)
user = serializer.save()
refresh = RefreshToken.for_user(user)
res = {
'refresh': str(refresh),
'access': str(refresh.access_token),
}
return response.Response(res, status.HTTP_201_CREATED)
創建一個urls文件,在 jwtauth/urls.py 加入:
from django.urls import path
from .views import registration
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path('register/', registration, name='register'),
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
現在會有一個新的端點: http://127.0.0.1:8000/api/jwtaut/register/
由於 jwtauth/view.py 中的permission_classes 設定為 IsAuthenticated 故出現 ‘detail’: ‘Authentication credentials were not provided.’這樣的內容。若改成AllowAny,即可使用。
4. 加上index.html與404.html頁面
建一個 main app
$ python manage.py startapp main
接著在 xsetting/settings.py 文件中 INSTALLED_APPS 加上’main’
由於新增 INSTALLED_APPS,終端機輸入:
$ python manage.py migrate
(1)新增index.html
由於目前 http://127.0.0.1:8000/ 沒有對應的頁面,所以加一個 index.html 檔案。在 serv/src 資料夾中加入一個 templates 資料夾,在其中加入 index.html
# index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=
, initial-scale=1.0">
<title>Django + JWT實作練習</title>
</head>
<body style="background-color: #f7e6e6; padding: 20px; font-family: 'Microsoft JhengHei', 'SimHei', 'STHeiti';">
<h1 style="font-family: 'Microsoft JhengHei', 'SimHei', 'STHeiti'; color: dimgray; margin-bottom: 30px;">Django + JWT實作練習</h1>
<p>可以參考以下網址</p>
<p>http://127.0.0.1:8000/</p>
<p>http://localhost:8000/api/notes/</p>
<p>http://127.0.0.1:8000/api/jwtauth/register/</p>
<p>http://127.0.0.1:8000/api/jwtauth/token/</p>
<p>http://127.0.0.1:8000/api/jwtauth/refresh/</p>
</body>
</html>
接著在 xsetting/settings.py 中,檔案上方引入 import os,並設定’DIRS’: [os.path.join(BASE_DIR,’templates’)]。
main 資料夾中加入 urls.py 檔案
main/urls.py 檔案加上
from django.urls import path
from . import views
urlpatterns = [
path('', views.main, name = 'home'),
]
main/views.py 檔案加上
def main(request):
return render(request, 'index.html', locals())
xsetting/urls.py 檔案加上
# urlpatterns 中加上
path('', include('main.urls')),
此時,http://127.0.0.1:8000/ 有對應的畫面。
(2)新增404.html
目前若輸入 http://127.0.0.1:8000/xxx (xxx為非對應的url)沒有對應的頁面,會出現 Django 404 除錯頁面。欲客製404頁面,可依循下列步驟。
在 xsetting/settings.py 中,把 DEBUG 設定改成 False,ALLOWED_HOSTS = [‘*’] 若為正式發行版本,會設置適當的domain name。
在 templates 資料夾,加上 404.html 檔案
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 Not Found</title>
</head>
<body style="background-color: #a5beb9; padding: 20px; font-family: 'Microsoft JhengHei', 'SimHei', 'STHeiti';">
<h1 style="font-family: 'Microsoft JhengHei', 'SimHei', 'STHeiti'; color: white; margin-bottom: 30px;">404 Not Found</h1>
<p>請檢查您輸入的網址</p>
<p>可以參考以下網址</p>
<p>http://127.0.0.1:8000/</p>
<p>http://localhost:8000/api/notes/</p>
<p>http://127.0.0.1:8000/api/jwtauth/register/</p>
<p>http://127.0.0.1:8000/api/jwtauth/token/</p>
<p>http://127.0.0.1:8000/api/jwtauth/refresh/</p>
</body>
</html>
main/views.py 檔案底部加上
def page_not_found_view(request, exception):
return render(request, '404.html')
xsetting/urls.py 檔案底部加上
handler404 = 'main.views.page_not_found_view'
此時,http://127.0.0.1:8000/xxx (xxx為非對應的url),會出現客製的404畫面。
5. 加上JS檔案
在 serv/src 中建立 static 資料夾,往下再建立 js 資料夾,其中建立 index.js 檔案。
index.js 加入
let i = 'test static js file';
console.log(i);
在 index.html 檔案中,最上面加上
{% load static %}
並最後結束 html 標籤前,加上
<script src="{% static 'js/index.js' %}"></script>
xsetting/settings.py 檔案,加入
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
.vscode/launch.json檔案中的args 加上 — insecure 設定
執行專案 終端機下指令也加上 insecure
$ python manage.py runserver --insecure
此時打開瀏覽器的開發者工具,網址輸入 http://127.0.0.1:8000/ 來到index頁面。在console可以看到順利印出 ‘test static js file’。
下一篇:🚪Django+JWT驗證(下)
To be continued.