使用者模組+jwt實現+註冊帶token值
阿新 • • 發佈:2020-11-23
1 JWT進階理解
1.1 settings.py
""" Django settings for opwf project. Generated by 'django-admin startproject' using Django 2.0.13. For more information on this file, see https://docs.djangoproject.com/en/2.0/topics/settings/ For the full list of settings and their values, see https://docs.djangoproject.com/en/2.0/ref/settings/ """ import datetime import os, sys # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, os.path.join(BASE_DIR, 'apps')) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'uorj1ni^mnut@wo@c%)iv)%5=8dxlml4-j0!f3b%4#f*8a5)3t' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = ['*'] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'corsheaders', 'user.apps.UserConfig', 'workflow.apps.WorkflowConfig', 'workerorder.apps.WorkerorderConfig', # 'jwt', # 'rest_framework_jwt', # 'rest_framework.authentication' ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'corsheaders.middleware.CorsMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'opwf.urls' CORS_ORIGIN_ALLOW_ALL = True TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'opwf.wsgi.application' # Database # https://docs.djangoproject.com/en/2.0/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'opwf_db', 'USER': 'root', 'PASSWORD': '1', 'HOST': '127.0.0.1', 'PORT': '3306' } } # Password validation # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators REST_FRAMEWORK = { # 文件報錯: AttributeError: ‘AutoSchema’ object has no attribute ‘get_link’ # 用下面的設定可以解決 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema', # 預設設定是: # 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.openapi.AutoSchema', # 異常處理器 # 'EXCEPTION_HANDLER': 'user.utils.exception_handler', # Base API policies 預設渲染器類 'DEFAULT_RENDERER_CLASSES': [ 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ], # 預設解析器類 'DEFAULT_PARSER_CLASSES': [ 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', 'rest_framework.parsers.MultiPartParser' ], # 1.認證器(全域性) 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # 在 DRF中配置JWT認證 # 'rest_framework.authentication.SessionAuthentication', # 使用session時的認證器 # 'rest_framework.authentication.BasicAuthentication' # 提交表單時的認證器 ], # 2.許可權配置(全域性): 順序靠上的嚴格 'DEFAULT_PERMISSION_CLASSES': [ # 'rest_framework.permissions.IsAdminUser', # 管理員可以訪問 # 'rest_framework.permissions.IsAuthenticated', # 認證使用者可以訪問 # 'rest_framework.permissions.IsAuthenticatedOrReadOnly', # 認證使用者可以訪問, 否則只能讀取 'rest_framework.permissions.AllowAny', # 所有使用者都可以訪問 ], # 3.限流(防爬蟲) 'DEFAULT_THROTTLE_CLASSES': [ 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle', ], # 3.1限流策略 # 'DEFAULT_THROTTLE_RATES': { # 'user': '100/hour', # 認證使用者每小時100次 # 'anon': '300/day', # 未認證使用者每天能訪問3次 # }, 'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation', 'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata', 'DEFAULT_VERSIONING_CLASS': None, # 4.分頁(全域性):全域性分頁器, 例如 省市區的資料自定義分頁器, 不需要分頁 # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', # 每頁返回數量 # 'PAGE_SIZE': 1 # 5.過濾器後端 'DEFAULT_FILTER_BACKENDS': [ 'django_filters.rest_framework.DjangoFilterBackend', # 'django_filters.rest_framework.backends.DjangoFilterBackend', 包路徑有變化 ], # 5.1過濾排序(全域性):Filtering 過濾排序 'SEARCH_PARAM': 'search', 'ORDERING_PARAM': 'ordering', 'NUM_PROXIES': None, # 6.版本控制:Versioning 介面版本控制 'DEFAULT_VERSION': None, 'ALLOWED_VERSIONS': None, 'VERSION_PARAM': 'version', # Authentication 認證 # 未認證使用者使用的使用者型別 'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser', # 未認證使用者使用的Token值 'UNAUTHENTICATED_TOKEN': None, # View configuration 'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name', 'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description', 'NON_FIELD_ERRORS_KEY': 'non_field_errors', # Testing 'TEST_REQUEST_RENDERER_CLASSES': [ 'rest_framework.renderers.MultiPartRenderer', 'rest_framework.renderers.JSONRenderer' ], 'TEST_REQUEST_DEFAULT_FORMAT': 'multipart', # Hyperlink settings 'URL_FORMAT_OVERRIDE': 'format', 'FORMAT_SUFFIX_KWARG': 'format', 'URL_FIELD_NAME': 'url', # Encoding 'UNICODE_JSON': True, 'COMPACT_JSON': True, 'STRICT_JSON': True, 'COERCE_DECIMAL_TO_STRING': True, 'UPLOADED_FILES_USE_URL': True, # Browseable API 'HTML_SELECT_CUTOFF': 1000, 'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...", # Schemas 'SCHEMA_COERCE_PATH_PK': True, 'SCHEMA_COERCE_METHOD_NAMES': { 'retrieve': 'read', 'destroy': 'delete' }, # 'Access-Control-Allow-Origin':'http://localhost:8080', # 'Access-Control-Allow-Credentials': True } AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/ LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True USE_TZ = False # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/ STATIC_URL = '/static/' AUTH_USER_MODEL = 'user.User' # jwt載荷中的有效期設定 JWT_AUTH = { # 1.token字首:headers中 Authorization 值的字首 'JWT_AUTH_HEADER_PREFIX': 'JWT', # 2.token有效期:一天有效 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), # 3.重新整理token:允許使用舊的token換新token 'JWT_ALLOW_REFRESH': True, # 4.token有效期:token在24小時內過期, 可續期token 'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(hours=24), # 5.自定義JWT載荷資訊:自定義返回格式,需要手工建立 'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler', }
1.2 user/utils.py
# -*- coding: utf-8 -*- def jwt_response_payload_handler(token, user=None, request=None, role=None): """ 自定義jwt認證成功返回資料 :token 返回的jwt :user 當前登入的使用者資訊[物件] :request 當前本次客戶端提交過來的資料 :role 角色 """ if user.first_name: name = user.first_name else: name = user.username return { 'authenticated': 'true', 'id': user.id, "role": role, 'name': name, 'username': user.username, 'email': user.email, 'token': token, }
1.3 user/views.py
import datetime import random from django.contrib.auth.hashers import make_password from django.shortcuts import render # Create your views here. from rest_framework import viewsets from rest_framework.response import Response from rest_framework.views import APIView from user.models import User from user.serializers import UserSerializer class UserViewSet(viewsets.ModelViewSet): queryset = User.objects.all() serializer_class = UserSerializer class RegisterView(APIView): def post(self, request): username = request.data.get('username') password = request.data.get('password') password_new = request.data.get('password_new') if not all([username, password, password_new]): return Response( {'msg': '資訊不全', 'code': 400} ) if password != password_new: return Response( {'code': 400, 'msg':'兩次登入密碼不一致'} ) user_serializer = UserSerializer(data=request.data) if user_serializer.is_valid(): user_serializer.save() user_info = User.objects.filter(username=username).first() return Response( {'msg': '註冊成功', 'code': 200, 'token': user_info.token} ) return Response( {'msg': '註冊失敗', 'error': user_serializer.errors } )
1.4 user/serializers.py
# -*- coding: utf-8 -*-
from rest_framework import serializers
from rest_framework_jwt.serializers import jwt_payload_handler
from rest_framework_jwt.settings import api_settings
from user.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
def create(self, data):
username = data.get('username', '')
password = data.get('password', '')
mobile = data.get('mobile', '')
email = data.get('email', '')
user = User(username=username, email=email, mobile=mobile)
user.set_password(password)
user.save()
# 補充生成記錄登入狀態的token
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
print(token)
user.token = token
user.save()
return user
1.5 user/urls.py
# -*- coding: utf-8 -*-
from django.urls import path
from rest_framework.routers import DefaultRouter
from rest_framework_jwt.views import obtain_jwt_token
from user import views
router = DefaultRouter()
router.register(r'user', views.UserViewSet)
urlpatterns = [
path('login/', obtain_jwt_token),
path('register/', views.RegisterView.as_view()),
]
urlpatterns += router.urls
2 效果展示
2.1 登入效果展示
http://192.168.56.100:1594/user/login/
2.2 註冊效果展示
http://192.168.56.100:1594/user/register/
2.2.1 必填欄位
2.2.2 效果展示
2.2.3 小海豚資料
3 前端專案規範
3.1 資料夾命名
- 全域性通用的元件放在 src/components 下
- 其他業務頁面中的元件,放在各自頁面下的 ./components 資料夾下
- 普通資料夾建議使用 kebab-case 方式命名
- 屬於 components 資料夾下的子資料夾,使用大寫字母開頭的 PascalBase風格
3.2 檔案命名規範
3.2.1 .js 問價命名規範
.js 檔案,使用 kebab-case 風格
屬於Api的,統一加上Api字尾
3.2.2 .vue 檔案命名規範
除index.vue之外, 其他 .vue 檔案統一用 PascalBase 風格
3.3 函式命名規範
3.3.1 後端配置路由規範
// 書籍管理介面
export const getBookList = (params, headers) => axios_get('/book/books/', params, headers)
export const addBook = (params, headers) => axios_get('/book/books/', params, headers)
export const updateBook = (params, headers) => axios_get('/book/books/', params, headers)
export const delBook = (params, headers) => axios_get('/book/books/', params, headers)
3.3.2 函式命名規範
handleLogin(){
}
getOtherQuery(query){
}
4 反射
- hasattr(ogj,name_str)判斷一個物件裡是否有對應的字串方法
- getattr(obj,name_str)根據字串去獲取obj物件裡的對應的方法的記憶體地址
- 使用stattr給類例項物件動態新增一個新的方法
- delattr刪除例項屬性
class Dog(object):
def eat(self, food):
print('eat method!!!', food)
d = Dog()
# hasatrr 判斷物件d是否有eat方法,有返回True,沒有返回False
print(hasattr(d, 'eat')) # True
print(hasattr(d, 'play')) # False
if hasattr(d, 'eat'):
func = getattr(d, 'eat')
func('baba')
# True
# False
# eat method!!! baba