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

背景

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

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

微信說明

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

oauth 2.0

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

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地址的完整路由。app

state

state爲oauth 2.0中容許的回調參數,state的構成爲: 客戶id,回調地址id,其餘須要回調參數微信公衆平臺

核心流程

圖片描述

核心代碼

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

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以後的步驟spa

@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)
相關文章
相關標籤/搜索