mpvue+django实现微信小程序授权登录

2018 年 11 月 05 日 • 阅读数: 179

mpvue+django实现微信小程序授权登录

简介

该项目前端是一个微信小程序的项目,使用mpvue构建;后端是一个django的项目,使用django-rest-framwork构建,在用户登录这一个方面我设计了2套登录逻辑,一个是传统的登录逻辑,使用账号和密码,获取JWT的方式,而另一个就是这篇文章所讲的使用微信小程序的接口的方式,实现用户的注册与登录

流程介绍

  • 客户端获取用户授权(自己写授权的逻辑)

  • 客户端获取到该微信用户基本信息(包括code验证码,encrypteDate加密后的信息,iv解密向量)

  • 客户端把获取的信息发送给服务器(自己的服务器,这里就是Django)

  • 服务器使用code去微信服务器换取session_key

  • 服务器用这个session_key+iv去解密用户密文, 得到用户完整信息(基本+openid)

  • 通过openid实现自己的注册与登录逻辑,返回用户一个登陆态(注意不能暴露用户的openid)

  • 用户下次请求携带登录态,后端就可以对其进行验证

总体流程图

整个微信小程序验证与登录的流程图如下(和微信官方的流程图有一定差异,因为加入了一些自己的具体实现)

具体实现

下面将一步一步的讲解整个登录逻辑,其中需要涉及到几个python的库,这里先提前说一下

python-weixin:封装了获取session_key的方法,使用这个库,就不需要我们自己写向微信服务器发请求的流程了,并且提供了生成微信的解密密钥的方法

rest_framework_jwt:生成IWT的一个第三方库,可以直接传入一个用户对象,它也可以和Django的用户认证相结合,非常方便

获取用户数据

页面实现如下,页面比较简单

就一个授权的按钮,点击后会弹出一个授权的提示框,点击确认就会触发 handleClose 函数

<i-button type="primary" open-type="getUserInfo" @click="handleOpen">
    授权登录
</i-button>
<i-modal :visible="visible" @ok="handleClose" @cancel="handleClose">
    <view>确认授权</view>
</i-modal>

JS实现如下,主要的逻辑在 handleClose 函数中实现

  • 先判断用户点击的内容,如果是确认在执行后面的操作

  • 通过 wx.login() 获取 code 验证码(该函数需要一个回调函数作为success的参数)

  • 在上面的success的回调函数中,我们调用 wx.getUserInfo() 获取用户信息(同样需要一个回调函数)

  • 在回调函数的参数中,我们可以获取到加密的用户信息 encryptedData 和解密 iv

  • 将我们获取到的三个数据都发送给后端(其中 login() 是我写的一个接口函数)

  data () {
    return {
      user: null,
      visible: false,
    }
  },
  methods: {
    handleClose(e) {
      this.visible = false;
      if ( e.mp.type == 'ok' ) {
        wx.login({ success: (res) => {
          const code = res.code;
          wx.getUserInfo({ success: (res) => {
            const encryptedData = res.encryptedData;
            const iv = res.iv;
            login({code:code, encryptedData:encryptedData, iv:iv})
              .then((res) => {
                console.log(res);
                this.user = res.data.userInfo;
                wx.setStorage({key:"token", data:res.data.token});
                wx.setStorage({key:"user", data:res.data.userInfo})
              })
            } });
          }})
      } else {
        console.log('cancel')
      }
    },
    handleOpen() {
      this.visible = true;
    },
  },

注意:login() 函数需要自己用 wx.request() 或者 flyio 实现

后端解密信息

先来看看后端的接口,django的基本URL的编写就不介绍了

urlpatterns = [
    ...
    # 微信授权
    path('api/weixin/', user_views.WeiXinView.as_view(), name='weixin'),
]

然后是 WeiXinView 的实现(注释已经详细解释了,就不写了o( ̄▽ ̄)o)

class WeiXinView(APIView):
    """
    微信授权接口
    """
    def post(self, request):
        # 获取用户信息
        data = get_weixin_user_info(request.data)
        user = UserProfile.objects.filter(username=data['username']).first()

        # 判断用户是否存在,若不存在则创建用户
        if not user:
            user = UserProfile.objects.create(**data)

        # 生成用户token,返回给前端
        token = self.create_token(user)
        serializer = UserDetailSerializer(user)
        data = serializer.data
        return Response({'token': token, 'userInfo': data}, status=status.HTTP_200_OK)

    @staticmethod
    def create_token(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)
        return token

获取用户信息的实现

import random
import string

from weixin import WXAPPAPI
from weixin.lib.wxcrypt import WXBizDataCrypt
from weixin.oauth2 import OAuth2AuthExchangeError

from photography.settings import APP_ID
from photography.settings import APP_SECRET

# 获取微信用户信息
def get_weixin_user_info(data):

    # 获取前端传递过来的三个关键的值
    code = data['code']
    iv = data['iv']
    encrypted_data = data['encryptedData']

    # 用配置文件中的配置生成API接口
    api = WXAPPAPI(appid=APP_ID, app_secret=APP_SECRET)
    try:
        # 使用code换取session_key
        session_info = api.exchange_code_for_session_key(code)
    except OAuth2AuthExchangeError as e:
        print(e, '验证失败,请重试')
    session_key = session_info.get('session_key')

    # 使用session_key生成密钥
    crypt = WXBizDataCrypt(APP_ID, session_key)
    try:
        # 解密得到用户信息
        user_info = crypt.decrypt(encrypted_data, iv)
    except UnicodeDecodeError as e:
        print(e, '请从新获取用户授权')

    _data = format_user_info(user_info)

    return _data


# 格式化用户信息
def format_user_info(user_info):
    data = {
        'username': user_info['openId'],
        'nick_name': user_info['nickName'],
        'gender': user_info['gender'],
        'image': user_info['avatarUrl'],
        'password': make_ran_str()
    }
    return data


# 生成随机密码
def make_ran_str():
    return ''.join(random.sample(string.ascii_letters + string.digits, 12))

缓存JWT与userInfo

在前面的JS中有这么两端代码,就是表示,将后端返回的数据保存到 storage

wx.setStorage({key:"token", data:res.data.token});
wx.setStorage({key:"user", data:res.data.userInfo})

至于其他的在什么时候使用这些数据,就可以根据具体情况决定了

标签: mpvueDjango

召唤伊斯特瓦尔