SaaS化實踐——如何用一個微信公衆號登錄多個不同的域名

背景

SaaS 作爲一種服務,需要爲不同的客戶定製不同的域名以滿足客戶定製化的需求。而微信登錄時需要填寫一個回調地址,單一的回調地址是難以處理多客戶域名的業務需求的,經過不同的 SaaS 項目的實踐,總結出了下面的方式。

微信登錄的核心代碼依然採用 psa 這個庫 https://github.com/python-soc...

微信說明

閱讀微信公衆平臺文檔,可以看到,當同一個微信公衆號需要在多個服務間使用時,微信的建議是提供一臺中控服務器,防止access_token的重複刷新,這個坑確實踩到過。

oauth 2.0

https://tools.ietf.org/html/r...

clipboard.png

圖片描述

核心概念、表結構

中控機

中控機爲同一引導用戶登錄的微信登錄服務器,其中此機器做的爲 oauth 2.0 截圖部分的 A、B,引導用戶授權,微信回調到此中控機,拿到code。
中控機通過state參數,解出customerid,根據customer配置找到回調地址。回調是將state,code帶去回調的客戶域名。

customer

customer表需要記錄微信的appid,appsecret,這樣即使客戶需要定製自己的微信公衆號,中控機也可以saas化。

redirecturl

由於微信的state參數長度有限,因此提供一張redirecturl表記錄回調地址,登錄時只需要將redirecturl_id帶入state中即可。redirecturl記錄的爲回調客戶域名+psa complete地址的完整路由。

state

state爲oauth 2.0中允許的回調參數,state的構成爲: 客戶id,回調地址id,其他需要回調參數

核心流程

圖片描述

核心代碼

中控機通過customer獲取對應的appid,secret。微信回調到cherrypick後,拿着code,state跳轉到對應的客戶域名。


def _auth(request, backend):
    cid = request.GET['cid']
    # TODO: DoesNotExist
    customer = Customer.objects.get(id=cid)
    appid, appsecret = customer.get_key_and_secret()
    log.info('login cid:%s, key:%s', cid, appid)
    def get_key_and_secret():
        return appid, appsecret
    request.backend.get_key_and_secret = get_key_and_secret
    return do_auth(request.backend)

@never_cache
@psa('our_social:cherrypick')
def auth(request, backend, key=''):
    return _auth(request, backend)
    
@never_cache
@psa()
def cherrypick(request, backend):
    code = request.GET.get('code', '')
    state = request.GET.get('state', '')
    redirect_url_id = state.split(',')[0]
    redirect_url = RedirectURL.objects.get(id=redirect_url_id).url
    redirect_url = '{}?code={}&state={}'.format(redirect_url, code, state)
    log.info('cherrypick, redirect_url: %s, state: %s', redirect_url, state)
    return redirect(redirect_url)    

SaaS 服務器處理 oauth 2.0 C、D之後的步驟

@psa('our_social:complete')
def complete(request, backend, *args, **kwargs):
    """Authentication complete view"""
    logout(request)
    state = request.GET.get('state', '') 
    ......
    state解析出cid等參數
    customer = Customer.objects.get(id=cid)
    appid, appsecret = product.get_key_and_secret()
    request._customer = customer
    覆蓋backend的方法
    def get_key_and_secret():
        log.info('login complete use appid: %s %s', appid, state)
    request.backend.get_key_and_secret = get_key_and_secret
    return do_complete(request.backend, _do_login, request.user,
                       redirect_name=REDIRECT_FIELD_NAME, request=request,
                       *args, **kwargs)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章