• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

微信小程序+Django—微信登录手机号码登录

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

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
                      

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap