Django + Taro 前後端分離專案實現企業微信登入

程式設計實驗室發表於2022-04-06

前言

還是最近在做的一個小專案,後端用的是Django搭配RestFramework做介面,前端第一次嘗試用京東開源的Taro框架來做多端(目前需要做用於企業微信的H5端和微信小程式)

本文記錄一下企業微信登入的流程,上週看文件看得頭暈暈的,其實很簡單,封裝好了就幾行程式碼的事~

兩種方式

  • 一種是先拼接好登入要用的地址authorize_url,回撥地址設定成h5應用的網頁入口,然後把authorize_url設定為企業微信裡的應用主頁就行,然後直接提取連結裡的code
  • 另一種是在應用裡拼接authorize_url地址,回撥地址同樣設定成h5應用的網頁入口,然後應用裡去請求authorize_url,然後提取連結裡的code用來登入就行

說是兩種,其實流程都是一樣的,只不過第一種少去了前端拼接authorize_url以及首次請求的操作,為了方便起見,本文推薦使用第一種

思路

假設前端地址是http://xxx.com,那麼我們用後端生成的企業微信登入地址中會把前端地址作為回撥地址傳入,在企業微信中訪問登入地址之後,回跳轉到我們的前端地址,並在路徑中附上引數code,形式如下:

http://xxx.com?code=dkwawen123j13bk1

所以前端要做的就是拿到這串code,並提交給後端,讓後端拿code去微信伺服器換使用者資訊,就這樣~

後端程式碼

企業微信登入的介面已經整合在我的「DjangoStarter」專案模板中,可以直接食用~

後端使用的是wechatpy這個庫,非常好用,封裝了微信開發的常用功能~

下面寫一下兩個關鍵的方法

from django.conf import settings
from django.contrib.auth import login
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
from drf_yasg2.utils import swagger_auto_schema
from drf_yasg2 import openapi
from rest_framework.exceptions import APIException
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.request import HttpRequest
from rest_framework.decorators import action
from wechatpy.enterprise import WeChatClient
from apps.core.serializers import UserSerializer

class WechatWork(viewsets.ViewSet):
    """微信企業號相關認證服務"""
    client = WeChatClient(
        settings.WECHAT_WORK_CONFIG['CORP_ID'],
        settings.WECHAT_WORK_CONFIG['SECRET'],
    )

    @swagger_auto_schema(operation_summary='獲取微信企業號登入連結')
    @action(detail=False)
    def get_authorize_url(self, request):
        return Response({
            # todo 這裡要寫上前端應用入口地址
            'url': self.client.oauth.authorize_url('http://xxx.com')
        })

    @swagger_auto_schema(
        operation_summary='通過code登入',
        manual_parameters=[
            openapi.Parameter(
                name='code', in_=openapi.IN_QUERY,
                description='從微信企業號伺服器獲取到的code',
                type=openapi.TYPE_STRING)
        ])
    @action(detail=False, methods=['POST'])
    def login_by_code(self, request: HttpRequest):
        code = request.GET.get('code', None)
        try:
            user_info = self.client.oauth.get_user_info(code)
        except Exception as e:
            raise APIException(detail=e)

        phone = user_info['UserId']
        is_created_user = False

        if User.objects.filter(username=phone).exists():
            user_obj: User = User.objects.get(username=phone)
        else:
            is_created_user = True
            user_obj: User = User.objects.create_user(username=phone, password=phone)

        # 記錄Django登入狀態
        login(request, user_obj)
        # 生成drf token
        token, created = Token.objects.get_or_create(user=user_obj)

        return Response({
            'user': UserSerializer(user_obj).data,
            'user_info': user_info,
            'successful': True,
            'is_created_user': is_created_user,
            'token': token.key,
            'message': '企業微信登入成功',
        })

寫完介面配置一下路由(這裡就不重複了)

然後請求這個get_authorize_url介面,得到一個地址

{
  "message": "請求成功",
  "code": 200,
  "data": {
    "url": "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx386...&redirect_uri=http%3A%2F%2Fxxx.com&response_type=code&scope=snsapi_base#wechat_redirect"
  }
}

比如我上面寫的應用入口地址是http://xxx.com,那麼得到的企業微信登入地址就是

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx386...&redirect_uri=http%3A%2F%2Fxxx.com&response_type=code&scope=snsapi_base#wechat_redirect

各個引數的意義看企業微信官方文件就行,我們不用細究

企業微信應用配置

接下來我們把這個地址設定成企業微信應用的主頁

如圖

同時還得設定一下「可信域名」,在同個頁面的最下方「開發者介面」處,把前端應用部署所在的伺服器域名和埠(80就不用)填上去就行~

這樣應用配置就好了

前端程式碼

前端用的是京東開源的Taro框架,我前一篇文章寫到我終於用上了React,說的就是在Taro開發裡用React+TypeScript,開發體驗非常好 (除了這個框架有一些讓人無語的坑之外)

前端要實現的就是從路徑引數裡取出code

我們看到,Taro官方文件就有關於路由引數的處理

所以可以這樣寫來獲取code(函式式元件寫法)

import { getCurrentInstance } from '@tarojs/taro'
let code getCurrentInstance().router?.params['code']

然而!這樣在普通頁面跳轉是可以的

比如這種形式

http://xxx.com/#/pages/index/index?code=abc

但人家微信登入回撥跳轉的地址形式是這樣

http://xxx.com?code=abc&state=#/pages/index/index

這根本就拿不到code啊 o(´^`)o

所以得自己用js封裝一個

直接上程式碼了

// 解析微信redirect_uri地址中的code
export const getCodeFromUrl = (url: string) => {
  let code = ''
  let index = url.indexOf('?')
  let paramStr = url.substring(index + 1, url.length);
  let params = paramStr.split('&')
  params.forEach(element => {
    if (element.indexOf('code') >= 0) {
      code = element.substring(element.indexOf('=') + 1, element.length)
    }
  });
  return code
}

使用的時候

let code = getCodeFromUrl(window.location.href)

就可以拿到code了

code都有了,後面就不用多說了~

參考資料

相關文章