微信小程序+Django—微信登錄手機號碼登錄


django + jwt 完成微信小程序身份驗證,步驟如下

環境說明:
1、小程序只需要拿到openid,其他信息不存儲。
2、Django自帶的User類不適合。需要對django user 進行擴展

流程

1.使用微信小程序登錄和獲取用戶信息Api接口
2.把Api獲取的用戶資料和code發送給django后端
3.通過微信接口把code換取成openid
4.后端將openid作為用戶名和密碼
5.后端通過JSON web token方式登錄,把token和用戶id傳回小程序
6.小程序將token和用戶id保存在storage中
7.下次請求需要驗證用戶身份的頁面時,在請求中加入token這個字段

 

微信小程序前端框架使用iview-weapp

https://github.com/TalkingData/iview-weapp

第一步注冊微信小程序

https://mp.weixin.qq.com/

第二步下載微信開發工具

http://www.ionic.wang/weixin/devtools/download.html

 

第三步新建小程序項目

 

項目結構:

 

修改app.js

新增小程序登錄頁面

 

打開項目pages目錄-->login 文件夾

修改login.js    login.json     login.wxml  login.wxss

login.js 代碼如下

// pages/login/login.js
const app = getApp()
const {
  $Toast
} = require('../../dist/base/index')
const {
  $Message
} = require('../../dist/base/index');
Page({

  /**
   * 頁面的初始數據
   */
  data: {
    yhxyVisible: false,
    ystkVisible: false
  },

  /**
   * 生命周期函數--監聽頁面加載
   */
  onLoad: function (options) {
    wx.getUserInfo({
      success: function (res) {
        var userInfo = res.userInfo
        var nickName = userInfo.nickName
        console.log(nickName)
        app.globalData.userInfo.nickName = nickName
        var avatarUrl = userInfo.avatarUrl
        app.globalData.userInfo.avatarUrl = avatarUrl
        var gender = userInfo.gender //性別 0:未知、1:男、2:女
        app.globalData.userInfo.gender = gender
        var province = userInfo.province
        var city = userInfo.city
        var country = userInfo.country
      }
    })

  },

  /**
   * 生命周期函數--監聽頁面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函數--監聽頁面顯示
   */
  onShow: function () {

  },

  /**
   * 生命周期函數--監聽頁面隱藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函數--監聽頁面卸載
   */
  onUnload: function () {

  },

  /**
   * 頁面相關事件處理函數--監聽用戶下拉動作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 頁面上拉觸底事件的處理函數
   */
  onReachBottom: function () {

  },

  /**
   * 用戶點擊右上角分享
   */
  onShareAppMessage: function () {

  },
  //手機號登錄
  mobileLogin(e) {
    $Toast({
      content: '登錄中...',
      type: 'loading',
      duration: 0,
      mask: false
    });
    console.log(e.detail.errMsg)
    console.log(e.detail.iv)
    console.log(e.detail.encryptedData)
    wx.login({
      success: res => {
        console.log(res)
        //請求后端換取openid的接口
        wx.request({
          url: 'http://199394.wezoz.com/landlorde/app/mobileLogin/',
          method: 'POST',
          data: {
            //將code和用戶基礎信息傳到后端
            jscode: res.code,
            iv: e.detail.iv,
            encryptedData: e.detail.encryptedData,
            nickname: app.globalData.userInfo.nickName,
            avatar_url: app.globalData.userInfo.avatarUrl,
            gender: app.globalData.userInfo.gender
          },
          success: res => {
            //獲取到openid作為賬號
            console.log("res=============", res)
            console.log(app.globalData.userInfo)
            if (res.data.code == 200 && res.data.msg == "ok") {
              //this.reFreshUserProfile()
              wx.setStorageSync('token', res.data.token)
              app.globalData.isLogin = true
              app.globalData.hasUserInfo = true
              app.globalData.token = res.data.token
              $Toast.hide();
              wx.redirectTo({
                url: '../index/index?id=1'
              })
            } else {
              wx.showToast({
                title: '網絡發生錯誤請稍后再試!',
                icon: 'error',
                duration: 2000
              })
            }
          }
        })
      }
    })
  },
  //微信登錄
  wxlogin() {
    $Toast({
      content: '登錄中...',
      type: 'loading',
      duration: 0,
      mask: false
    });
    wx.login({
      success: res => {
        console.log(res)
        //請求后端換取openid的接口
        wx.request({
          url: 'http://199394.wezoz.com/landlorde/app/wxlogin/',
          method: 'POST',
          data: {
            //將code傳到后端
            jscode: res.code
          },
          success: res => {
            //獲取到openid作為賬號
            console.log("res=============", res)
            console.log(app.globalData.userInfo)
            if (res.data.code == 200 && res.data.msg == "ok") {
              //this.reFreshUserProfile()
              wx.setStorageSync('token', res.data.token)
              app.globalData.isLogin = true
              app.globalData.hasUserInfo = true
              app.globalData.token = res.data.token
              $Toast.hide();
              wx.redirectTo({
                url: '../index/index?id=1'
              })
            } else {
              wx.showToast({
                title: '登錄失敗請稍后再試!',
                icon: 'error',
                duration: 2000
              })
            }
          }
        })
      }
    })
  },
  yhxy() {
    this.setData({
      yhxyVisible: true
    });
  },
  yhxyok() {
    this.setData({
      yhxyVisible: false
    });
  },
  yhxycancel() {
    this.setData({
      yhxyVisible: false
    });
  },
  ystk() {
    this.setData({
      ystkVisible: true
    });
  },
  ystkok() {
    this.setData({
      ystkVisible: false
    });
  },
  ystkcancel() {
    this.setData({
      ystkVisible: false
    });
  },
})

 

 login.json

{
  "navigationBarTitleText": "登錄",
  "usingComponents": {
    "i-button": "../../dist/button/index",
    "i-panel": "../../dist/panel/index",
    "i-avatar": "../../dist/avatar/index",
    "i-row": "../../dist/row/index",
    "i-col": "../../dist/col/index",
    "i-toast": "../../dist/toast/index",
    "i-modal": "../../dist/modal/index",
    "i-message": "../../dist/message/index"
  }
}

 

 login.wxml

<!--pages/login/login.wxml-->
<view class="container">
    <image class="logStyle" src="http://img11.360buyimg.com/mobilecms/s700x256_jfs/t20539/229/82605291/66559/df347eb5/5af96a18N9451b1a1.jpg"></image>
    <i-button bindgetphonenumber="mobileLogin" style="background: #ff6700;" class="btnLoginStyle" open-type="getPhoneNumber" type="warning" shape="circle" size="large">手機快捷登錄</i-button>
  <text class="weChatLogin" bindtap="wxlogin">微信登錄</text>
</view>
<view class="company">
  <view>登錄/注冊代表您已閱讀並同意 <text class="descStyle" bindtap="yhxy">用戶協議</text><text class="descStyle" bindtap="ystk">隱私條款</text> </view>
</view>
<i-toast id="toast" />

<i-modal title="用戶協議" visible="{{ yhxyVisible }}" bind:ok="yhxyok" bind:cancel="yhxycancel">
  <view>1.用戶協議用戶協議用戶協議</view>
  <view>2.用戶協議用戶協議用戶協議</view>
  <view>3.用戶協議用戶協議用戶協議</view>
</i-modal>


<i-modal title="隱私條款" visible="{{ ystkVisible }}" bind:ok="ystkok" bind:cancel="ystkcancel">
  <view>1.隱私條款隱私條款隱私條款</view>
  <view>2.隱私條款隱私條款隱私條款</view>
  <view>3.隱私條款隱私條款隱私條款</view>
</i-modal>

login.wxss

/* pages/login/login.wxss */
.item {
  height: 100px;
  text-align: center;
}

.container {
  display: block;
  width: 200px;
  height: 100px;
  margin: 0 auto;
  position: relative;
  text-align: center;
}

.logStyle {
  width: 100px;
  height: 100px;
  background-color: #eeeeee;
  margin-top: 90rpx;
  margin-bottom: 50rpx;
  border-radius: 50%;
}
.i-btn-warning{
  background: #ff6700 !important;
}
.descStyle {
  color: #ff6700;
}
.company {
  position: absolute;
  bottom: 30rpx;
  width: 100%;
  display: flex;
  justify-content: center;
  font-size: 12px;
}

.weChatLogin{
  font-size: 14px;
}

 

第四步新建django項目

可參照微信開發入門篇一  https://www.cnblogs.com/wangcongxing/p/11546780.html

 

第五步處理微信登錄

 打開django項目,結構如下

 

authentication.py
from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from rest_framework_jwt.settings import api_settings


class UserAuthentication(BaseAuthentication):
    def authenticate(self, request):
        if 'token' in request.data:
            try:
                token = request.data['token']
                jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
                user_dict = jwt_decode_handler(token)
                return (user_dict, token)
            except Exception as ex:
                raise exceptions.AuthenticationFailed(detail={'code': 401, 'msg': 'skey已過期'})
        else:
            raise exceptions.AuthenticationFailed(detail={'code': 400, 'msg': '缺少token'})

    def authenticate_header(self, request):
        return 'skey'
WXBizDataCrypt.py
import base64
import json
from Crypto.Cipher import AES

class WXBizDataCrypt:
    def __init__(self, appId, sessionKey):
        self.appId = appId
        self.sessionKey = sessionKey

    def decrypt(self, encryptedData, iv):
        # base64 decode
        sessionKey = base64.b64decode(self.sessionKey)
        encryptedData = base64.b64decode(encryptedData)
        iv = base64.b64decode(iv)

        cipher = AES.new(sessionKey, AES.MODE_CBC, iv)

        decrypted = json.loads(self._unpad(cipher.decrypt(encryptedData)))

        if decrypted['watermark']['appid'] != self.appId:
            raise Exception('Invalid Buffer')

        return decrypted

    def _unpad(self, s):
        return s[:-ord(s[len(s)-1:])]
urls.py
from django.contrib import admin
from django.urls import path
from django.urls import path, include
from app import views
from app import test_view

urlpatterns = [
    # 微信小程序登錄
    path('wxlogin/', views.code2Session),
    # 微信手機號碼登錄
    path('mobileLogin/', views.mobileLogin),
    # 通過token獲取用轉換用戶信息
    path('checkToken/', views.checkToken),
]
models.py
from django.db import models

# Create your models here.
from django.contrib.auth.models import AbstractUser
from django.db import models


class NewUser(AbstractUser):
    nickname = models.CharField(max_length=225, verbose_name="昵稱", default="")
    avatar_url = models.CharField(max_length=225, verbose_name="頭像", default="")
    gender = models.CharField(max_length=225, verbose_name="性別", default="")
    session_key = models.CharField(max_length=225, verbose_name="session_key", default="")
    mobilePhoneNumber = models.CharField(max_length=225, verbose_name="手機號碼", default="")
views.py
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
# Create your views here.
from django.http import HttpResponse
from django.contrib.auth.models import Permission, User
from django.contrib import auth
from django.views.decorators.http import require_http_methods
from rest_framework.views import APIView
from rest_framework_jwt.settings import api_settings
from django.core import serializers
from django.http import JsonResponse, HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect
import uuid, datetime, json, time
from datetime import timedelta
from dateutil.relativedelta import relativedelta
from wechatpy import WeChatClient
import json, re, urllib3
import os
import json
from wechatpy import WeChatPay
from django.views.decorators.csrf import csrf_exempt
import time
import random
from django.core.cache import cache  # 引入緩存模塊
from wechatpy.utils import timezone
from django.db import transaction
from django import forms
from django.db.models import F
import requests
from django.contrib.auth.models import Permission, User
from django.db import connection
from django.db.models import Sum


@csrf_exempt
def login(request):
    if request.method == "POST":
        username = request.POST.get('username')
        passwd = request.POST.get('passwd')

        user = User.objects.filter(username=username).first()
        auth.login(request, user)
        # 登錄成功
        print(username)
        print(passwd)
        return HttpResponse("登錄成功!")
    else:
        return HttpResponse("請求錯誤")

#小程序appid
appid = "wxxxxxxxxx"
# 小程序秘鑰
appsecret = "xxxxxxxxxxxx"

'''
登錄函數:
'''


@require_http_methods(['POST'])
@csrf_exempt
def GetOpenIdView(request):
    data = json.loads(request.body)
    jscode = data['jscode']

    openid, session_key = OpenId(jscode).get_openid()
    return JsonResponse({
        'openid': openid,
        'session_key': session_key
    })


from app import models


@require_http_methods(['POST'])
@csrf_exempt
def login_or_create_account(request):
    data = json.loads(request.body)
    print(data)
    openid = data['openid']
    nickname = data['nickname']
    avatar_url = data['avatar_url']
    gender = data['gender']

    try:
        user = models.NewUser.objects.get(username=openid)
    except models.NewUser.DoesNotExist:
        user = None

    if user:
        user = models.NewUser.objects.get(username=openid)
    else:
        user = models.NewUser.objects.create(
            username=openid,
            password=openid,
            nickname=nickname,
            avatar_url=avatar_url,
            gender=gender
        )

    jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
    jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
    payload = jwt_payload_handler(user)
    token = jwt_encode_handler(payload)
    res = {
        'status': 'success',
        'nickname': user.nickname,
        'user_id': user.id,
        'token': token
    }
    return JsonResponse(res)


class OpenId:
    def __init__(self, jscode):
        self.url = 'https://api.weixin.qq.com/sns/jscode2session'
        self.app_id = appid
        self.app_secret = appsecret
        self.jscode = jscode

    def get_openid(self):
        url = self.url + "?appid=" + self.app_id + "&secret=" + self.app_secret + "&js_code=" + self.jscode + "&grant_type=authorization_code"
        res = requests.get(url)
        try:
            openid = res.json()['openid']
            session_key = res.json()['session_key']
        except KeyError:
            return 'fail'
        else:
            return openid, session_key


import hashlib
import json
import requests
from rest_framework import status
from rest_framework.decorators import api_view, authentication_classes
from rest_framework.response import Response
from django_redis import get_redis_connection
from app.WXBizDataCrypt import WXBizDataCrypt

appid = ""
secret = ""


@api_view(['POST'])
@authentication_classes([])  # 添加
def code2Session(request):
    js_code = request.data['jscode']
    url = 'https://api.weixin.qq.com/sns/jscode2session' + '?appid=' + appid + '&secret=' + secret + '&js_code=' + js_code + '&grant_type=authorization_code'
    response = json.loads(requests.get(url).content)  # 將json數據包轉成字典
    if 'errcode' in response:
        # 有錯誤碼
        return Response(data={'code': response['errcode'], 'msg': response['errmsg']})
    # 登錄成功
    openid = response['openid']
    session_key = response['session_key']
    # 保存openid, 需要先判斷數據庫中有沒有這個openid
    user = models.NewUser.objects.filter(username=openid).first()
    if user is None:
        user = models.NewUser.objects.create(
            username=openid,
            password=uuid.uuid4(),
            session_key=session_key
        )
    else:
        user.session_key = session_key
        user.save()

    # 將自定義登錄態保存到緩存中, 兩個小時過期
    jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
    jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
    payload = jwt_payload_handler(user)
    skey = jwt_encode_handler(payload)
    print(skey)
    return JsonResponse({'code': 200, 'msg': 'ok', 'token': skey})


def filter_emoji(desstr, restr=''):
    # 過濾表情
    try:
        res = re.compile(u'[\U00010000-\U0010ffff]')
    except re.error:
        res = re.compile(u'[\uD800-\uDBFF][\uDC00-\uDFFF]')
    return res.sub(restr, desstr)


@api_view(['POST'])
@authentication_classes([])  # 添加
def mobileLogin(request):
    js_code = request.data['jscode']
    iv = request.data["iv"]
    encryptedData = request.data["encryptedData"]
    nickname = request.data["nickname"]
    avatar_url = request.data["avatar_url"]
    gender = request.data["gender"]
    nickname = filter_emoji(nickname, '')
    if js_code is None or iv is None or encryptedData is None or avatar_url is None:
        return JsonResponse({'code': 400, 'msg': '系統維護,請稍后再試!'})
    url = 'https://api.weixin.qq.com/sns/jscode2session' + '?appid=' + appid + '&secret=' + secret + '&js_code=' + js_code + '&grant_type=authorization_code'
    response = json.loads(requests.get(url).content)  # 將json數據包轉成字典
    if 'errcode' in response:
        # 有錯誤碼
        return JsonResponse({'code': response['errcode'], 'msg': response['errmsg']})
    try:
        openid = response['openid']
        session_key = response['session_key']
        wxdc = WXBizDataCrypt(appid, session_key)
        pResult = wxdc.decrypt(encryptedData, iv)
        print(pResult)
        # 保存openid, 需要先判斷數據庫中有沒有這個openid
        user = models.NewUser.objects.filter(username=openid).first()
        if user is None:
            user = models.NewUser.objects.create(
                username=openid,
                password=uuid.uuid4(),
                session_key=session_key,
                nickname=nickname,
                avatar_url=avatar_url,
                gender=gender,
                mobilePhoneNumber=pResult["phoneNumber"]
            )
        else:
            user.session_key = session_key
            user.nickname = nickname,
            user.avatar_url = avatar_url,
            user.gender = gender
            user.save()
        # token有效期1天,settings.py 文件中設置
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        print(token)
        return JsonResponse({'code': 200, 'msg': 'ok', 'token': token})
    except Exception as ex:
        return JsonResponse({'code': 400, 'msg': "發生錯誤請稍后再試!"})


from app import authentication


@api_view(['POST'])
@authentication_classes([authentication.UserAuthentication])  # 添加
def checkToken(request):
    print(request.user)
    print(request.user["username"])
    return JsonResponse({'code': 200, 'msg': 'ok', 'userInfo': request.user})
settings.py
"""
Django settings for landlorde project.

Generated by 'django-admin startproject' using Django 3.0.5.

For more information on this file, see
https://docs.djangoproject.com/en/3.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.0/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '1mf87-kx7x6*zoio^f6@8oqu*t=**pmv*i^kduz*hc)iw4r(5q'

# 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',
    'app.apps.AppConfig',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'landlorde.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        '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 = 'landlorde.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'NAME': 'landlorde',
        'USER': 'root',
        'PASSWORD': '123456'
    }
}
CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379',
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        },
    },
}
REDIS_TIMEOUT = 7 * 24 * 60 * 60
CUBES_REDIS_TIMEOUT = 60 * 60
NEVER_REDIS_TIMEOUT = 365 * 24 * 60 * 60
# 開發redis 路徑 C:\Program Files\Redis redis-server redis.windows.conf
'''
windows下安裝Redis第一次啟動報錯:

[2368] 21 Apr 02:57:05.611 # Creating Server TCP listening socket 127.0.0.1:6379: bind: No error
解決方法:在命令行中運行
redis-cli.exe
127.0.0.1:6379>shutdown
not connected>exit
然后重新運行redis-server.exe redis.windows.conf
'''
'''
測試地址
http://199394.wezoz.com/landlorde/app/get-openid/

http://199394.wezoz.com/landlorde/app/wx-login/
'''
# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators

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.2/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.2/howto/static-files/

STATIC_URL = '/landlordestatic/'

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'landlordestatic'),
)
# SIMPLEUI 配置
SIMPLEUI_STATIC_OFFLINE = True
SIMPLEUI_HOME_INFO = False

# 圖片上傳路徑
MEDIA_URL = '/'
MEDIA_ROOT = r'D:\landlordestatic'

# 服務號配置
domain = "http://www.xxxx.com/"

keysPath = os.path.join(BASE_DIR, 'keys')

AUTH_USER_MODEL = "app.NewUser"

weChatConfig = {
    'appid': "wxxxxxxxxxx",
    'appsecret': "xxxxxxxxxxxxxxx",
    'mch_id': "xxxxxxxx",
    'notify_url': domain + '/notify_url/',
    'baiduapiak': 'xxxxxxxxxxxxxxx',
    'baiduapiwebak': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    'mch_cert': os.path.join(keysPath, r"apiclient_cert.pem"),
    'mch_key': os.path.join(keysPath, r"apiclient_key.pem"),
}


# 在末尾添加上
import datetime

# 在末尾添加上
'''
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',# JWT認證,在前面的認證方案優先
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}
'''

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), #JWT_EXPIRATION_DELTA 指明token的有效期
}


REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'app.authentication.UserAuthentication',  # 用自定義的認證類
    ),
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
    ),
    'DEFAULT_PARSER_CLASSES': (
        'rest_framework.parsers.JSONParser',
    ),
}

 requirements.txt

amqp==1.4.9
anyjson==0.3.3
asgiref==3.2.7
billiard==3.3.0.23
celery==3.1.26.post2
certifi==2019.9.11
cffi==1.13.2
chardet==3.0.4
cryptography==2.8
defusedxml==0.6.0
diff-match-patch==20181111
Django==2.1.11
django-import-export==1.2.0
django-redis==4.10.0
django-simpleui==3.4
django-timezone-field==3.1
djangorestframework==3.11.0
djangorestframework-jwt==1.11.0
dnspython==1.16.0
docopt==0.6.2
et-xmlfile==1.0.1
greenlet==0.4.15
idna==2.8
importlib-metadata==0.23
jdcal==1.4.1
kombu==3.0.37
MarkupPy==1.14
monotonic==1.5
more-itertools==7.2.0
odfpy==1.4.0
openpyxl==3.0.1
optionaldict==0.1.1
Pillow==6.2.1
pycparser==2.19
pycryptodome==3.9.4
PyJWT==1.7.1
PyMySQL==0.9.3
pyOpenSSL==19.1.0
python-crontab==2.4.0
python-dateutil==2.8.1
pytz==2019.3
PyYAML==5.1.2
records==0.5.3
redis==3.3.11
redlock-py==1.0.8
requests==2.22.0
six==1.13.0
SQLAlchemy==1.3.11
sqlparse==0.3.0
tablib==0.14.0
urllib3==1.25.7
vine==1.3.0
wechatpy==1.8.3
xlrd==1.2.0
xlwt==1.3.0
xmltodict==0.12.0
zipp==0.6.0

 

最終效果:

 

控制台輸出

 

微信開發工具控制台輸出

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM