Django rest framework 实现微信小程序登陆

最近接触一款小程序开发,捋了捋微信小程序作为前端和Django作为后端的需求,尝试写了一个登陆模块,代码是从项目分离出来的,所以部分地方需要自行匹配和修改,仅供参考。

1. 运行环境

这里使用的是Django2.x

2. 配置数据库表

这里需要对Django自带的django.contrib.auth.models.User模块进行扩展,我们选择一对一关键的形式对User进行扩展,才能存储微信小程序中所返回的一些字段。

# your_app/models.py

from django.contrib.auth.models import User
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save


"""
    这里举几个字段,如果需要其他的字段请自行添加
"""
class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    avatarUrl = models.CharField(max_length=250)
    gender = models.IntegerField(default=1)
    nickName = models.CharField(max_length=100)

# 钩子函数,用于创建和保存User时调用该函数实现Profile的同步关联
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()

设计一个简单的序列器,这边直接使用最简单的方式

# your_app/serializer.py

def serializer(user):
user_dict = {
    'userId' : user.id,
    'nickName' : user.profile.nickName,
    'gender' : user.profile.gender,
    'avatarUrl': user.profile.avatarUrl,
    'faceId' : user.profile.faceId,
    'realName': user.profile.realName,
    'number' : user.profile.number,
    'faceImage' : user.profile.faceImage,
    'taskId': user.profile.taskId,
    'faceTag': user.profile.faceTag
}
return user_dict

3. 配置文件

在项目的setting.py文件下加入微信小程序的app_id和app_secret

# your_app/setting.py

WX_LOGIN_DICT = {
    'url' : 'https://api.weixin.qq.com/sns/jscode2session',
    'app_id': '写你申请到的app_id',
    'app_secret': '写你申请到的app_secret',
}

在项目的setting.py文件下加入rest framework的相关配置

import datetime

# drf框架的配置信息
REST_FRAMEWORK = {
    # 设置所有接口都需要被验证,如果需要开放部分接口请注释掉,自行到ApiView中进行认证
    'DEFAULT_PERMISSION_CLASSES': (
        rest_framework.permissions.IsAuthenticatedOrReadOnly,
    ),
    # 用户登陆认证方式
    'DEFAULT_AUTHENTICATION_CLASSES': (
        # 考虑到是微信小程序,这边仅开发jwt认证方式
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        #’rest_framework.authentication.SessionAuthentication’,
        #’rest_framework.authentication.BasicAuthentication’,
    ),
}

# 设置token有效期最长天数为7天
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
}

4. 设计登陆小工具

# your_app/wx_utils.py

class OpenId:
    def __init__(self, app_dict, jscode):
        self.url = app_dict.get('url')
        self.app_id = app_dict.get('app_id')
        self.app_secret = app_dict.get('app_secret')
        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

5. 视图函数设计

这边为了方便,把获取的微信小程序openid都设置成账号和密码,只是个教程而且,请同学们日常开发中还是要注意安全问题

# your_app/views.py

from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from classin_django.settings import WX_LOGIN_DICT as wx_login_dict
from .wx_utils import OpenId
from django.contrib.auth.models import User
from rest_framework_jwt.settings import api_settings
import json
from .serializer import serializer as UserSerializer
# Create your views here.


@csrf_exempt
def test_wx(request):
    # 获取code
    print('get code')
    data = json.loads(request.body)
    jscode = data.get('jscode')
    openid, session_key = OpenId(wx_login_dict, jscode).get_openid()

    print(openid, session_key)
    return JsonResponse({
        'openid': openid,
        'session_key': session_key
    })

@csrf_exempt
def wx_login(request):
    print('login')
    data = json.loads(request.body)
    print(data)
    openid = data.get('openid')
    try:
        user = User.objects.get(username=openid)
    except User.DoesNotExist:
        user = None

    if user:
        user = User.objects.get(username=openid)
        print('用户已经存在,直接登陆')
    else:
        print('用户未存在,注册用户')
        user = User.objects.create_user(
            username = openid,
            password = openid,
            email = 'none1@qq.com'
        )
        # 保存微信小程序返回的信息
        user.profile.avatarUrl = data.get('avatar_url')
        user.profile.gender = data.get('gender')
        user.profile.nickName = data.get('nickname')
        user.save()

    # 序列化
    serializer = UserSerializer(user)
    # 签发token
    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',
        'token': token,
        'user':serializer
    }
    print(token)
    return JsonResponse(res)

6. 设计route

在你的app目录下创建urls.py

# your_app/urls.py
from . import views
from django.urls import path


urlpatterns = [
    path('getcode', views.test_wx),
    path('login', views.wx_login),
]

在项目的总路由下添加urls

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('users/', include('users.urls')),
]

7. 微信登陆功能设计

这边把登陆功能在app.js中,也可以放置于其他位置,自行处理

login: function(){
    // 登录
    var that = this
    console.log(storage_tag.msg)
    wx.login({
      success: res => {
        // 发送 code 到后台换取数据
        wx.request({
          url: 'localhost:8000/users/getcode',
          data: {
            jscode: res.code
          },
          header: {},
          method: 'POST',
          dataType: 'json',
          responseType: 'text',
          success: function (res) {
            console.log(res)
            console.log('userInfo:' + that.globalData.userInfo)
            wx.request({
              url: api.login,
              data: {
                openid: res.data.openid,
                session_key: res.data.session_key,
                nickname: that.globalData.userInfo.nickName,
                avatar_url: that.globalData.userInfo.avatarUrl,
                gender: that.globalData.userInfo.gender
              },
              header: {},
              method: 'POST',
              dataType: 'json',
              responseType: 'text',
              success: function (res) {
                if(res.statusCode==200){
                  console.log(res)
                  console.log(res.data.token)
                  # 存储token
                  wx.setStorageSync('jwt_token', res.data.token)
                  that.globalData.isLogin = true
                  that.globalData.checkLogin = true
                  that.globalData.headers = {
                    "Content-Type": "application/json",
                    "Authorization": "jwt " + res.data.token
                  }
                }
                //由于这里是网络请求,可能会在 Page.onLoad 之后才返回
                // 所以此处加入 callback 以防止这种情况
                if (that.checkLoginReadyCallback) {
                  that.checkLoginReadyCallback(res);
                }

              },
              fail: function (res) {
                console.error('访问错误')
              },
              complete: function (res) { },
            })
          },
          fail: function (res) {
            console.error(res)
          },
          complete: function (res) {

          },
        })
      }
    })
  },

登陆成功后返回的数据格式长这样:

版权声明:如无特殊说明,文章均为本站原创,转载请注明出处

本文链接:http://tunm.top/article/wx_django/