Web應用多帳號系統設計及微信掃碼登陸實現

1 前言概述

公司對功能測試,性能測試,安全測試等等都作了比較好的自動化後,急須要一個MIS系統來統一管理這些結果及報表。python

此MIS系統特色以下:web

  • 僅內部人員使用
  • 部署在公網

基於如上特色,顯然讓公司的人爲這樣一個內部系統而實現一個完整的帳號不太現實,要兼顧隱私性和便捷性的需求,做者想到了使用微信掃碼登陸來作爲身份認證,而後後臺管理員審覈,這樣就能夠達到以下的效果:數據庫

  • 能夠實現無門檻註冊(微信掃一掃就完成註冊),保證了便捷性
  • 系統對未審覈經過的人員進行隔離,保證了隱私性

而後在開發完畢此係統後,以爲有必要再總結並且小升華一下,因而就有了本文的寫做動機。瀏覽器

2 多帳號原理

原本本文的目的是 「實現微信掃碼登陸」 ,可是後來以爲僅僅是爲了實現這個功能,本文的立意又顯得過低。因此就在此處擴展一下爲 「多帳號受權登陸系統」安全

在近些年來,隨着互聯網愈來愈開發和協做,目前的系統登陸方式也愈來愈多,已經遠遠超越了之前的單用戶名的方式了。除了用戶名密碼外,通常網站還提供以下的登陸方式:服務器

    • 第三方受權微信

      微信/QQ/新浪微博(國內)Google/Facebook/Github(國外)session

    • 綁定帳號app

      手機號郵箱號dom

基於如上的幾種登陸方式,就造成了以下的 「多帳號登陸體系圖」

img

基本原理:

    • 第三方受權

      可以從可信第三方獲取到相應的返回值(用戶信息),而後和 user_id 綁定不須要額外再輸入密碼便完成鑑權過程會建立一組從此能夠修改的 user_id 做爲 佔位用戶鑑權成功後設置session狀態

    • 綁定帳號

      事先已經完成了 user_id 的註冊完成了相應帳號綁定,即表示承認和 user_id 均能登陸使用和 user_id 一樣的或者不一樣的密碼體系(通常使用相同密碼)登陸完成鑑權鑑權成功後設置session狀態

關於 綁定帳號 的方式比較簡單,此處就再也不贅述。

基於 第三方受權 的方式,則比較精妙,可學習性比較強,由於基於互聯網愈來愈開放的特性,此方式確定會愈來愈多的被應用,愈來愈成爲主流。下面將以 微信掃碼 受權登陸爲例子來進行講解。

3 掃碼登陸邏輯

使用 微信掃碼 受權登陸的邏輯圖以下:

img

其中主要處理的事情以下:

  • 向第三方發起鑑權請求
  • 第三方鑑權回調
  • 和MIS系統本地 user_id 體系關聯(新建用戶)
  • 設置session登陸狀態
  • 處理不一樣結果的顯示界面

4 微信掃碼過程

使用過微信掃碼登陸系統的人會有以下的過程體驗(以著名社交網站 知乎 爲例子):

  1. 打開 知乎 主頁,點擊 「微信登陸」 的圖標
  2. 瀏覽器重定向到微信域(見下圖標記1)下面的二維碼頁面
  3. 用戶掏出手機打開微信,掃一掃
  4. 在手機微信上點擊受權
  5. PC上面的二維碼頁面顯示受權成功,並轉向到 知乎 首頁,認證成功

整個過程對於終端用戶來講,只有短短几秒,並且不用輸入任何密碼,能夠說是一種很是安全又便捷的體驗。

那麼問題來了,經過微信掃描二維碼,並完成MIS系統註冊登陸這個短短几秒的時間裏面,到底發生了哪些事情?

經過瀏覽器抓包,對幾個關鍵通信過程進行分析。

img

PC瀏覽器會依次發起兩個長鏈接(比較長時處於 pending 狀態)的請求:

  • 等待手機端的微信掃碼(上圖標記2)
  • 等待手機微信點擊 「確認登陸」 按鈕(上圖標記3)

這兩個狀態都會反饋到PC端的二維碼頁面,在手機端完成確認後,PC瀏覽器上面的頁面就會生定向到受權後的頁面(如 知乎 首頁)。

具體各方通信時序圖以下:

img

上圖對整個過程當中通信涉及的對象進行了清楚的描述,關於上圖數字標註部分註解以下:

  1. 網站服務器向微信API傳入帶有 回調url 的參數
  2. 手機微信經過攝像頭掃二維碼,從 光學原理 上完成數據的傳遞
  3. PC瀏覽器上查詢掃碼狀態的長鏈接收到返回的狀態值,並更新提示
  4. PC瀏覽器上查詢手機客戶端點擊確認按鈕的狀態值,並更新提示,而後重定向到 過程1 中傳遞url地址上
  5. 網站服務器在受權成功後,完成本系統的用戶註冊或者登陸的業務邏輯
  6. 網站服務器重定向到用戶登陸成功的界面中(若是對於新註冊用戶不須要額外的審覈的話)

關於微信掃碼認證部分的開發,本文再也不贅述,只給出以下注意事項:

  • 微信平臺的各類API接口請參考:微信開放平臺提供的官方文檔
  • 微信掃碼登陸的開發權限須要在微信開放平臺中進行企業資質認證(我的用戶沒法得到)
  • 回調url 的域必需在微信開放平臺中進行填寫備案,本地開發時傳遞的 回調url 參數必須和備案一致

5 代碼實現

根據如上原理,最後將提供具體實現代碼以供參考 ,爲了簡潔,有一些通用的工具函數的具體實現就不貼出來了。

使用 python3.5 實現 微信掃碼登陸Web應用程序 的參考代碼以下所示。

對應 上圖標識1 中的代碼實現:

class WeChatAuth(MyBaseHandler):
    """ 點擊後直接重定向到微信登陸界面 - wechat QR掃碼登陸,web端 - 直接重定向到微信的頁面 """

    def get(self):
        state = get_uuid1_key()  # 生成惟一的碼

        wx_qr_param = dict(
            appid=wx_webapp.appid,
            # redirect_uri=wx_webapp.qr_auth_cb_url,
            redirect_uri='http://your.domain.com/wechat/wechat-auth-callback/',
            response_type='code',
            scope=wx_webapp.login_scope,
            state=state
        )  ##wechat_redirect

        wx_qr_url = 'https://open.weixin.qq.com/connect/qrconnect?%s#wechat_redirect' \
                    % urllib.parse.urlencode(wx_qr_param)

        self.redirect(wx_qr_url)
        
複製代碼
class WeChatAuthCallback(MyBaseHandler):
    """ 微信第三方認證以後,開始將此用戶在本系統沉澱下來 - 用於微信服務器傳回code的值 - 此處要再請求得到access_token """

    async def get(self):
        wx_code = self.get_argument('code', '')
        wx_state = self.get_argument('state', '')

        if wx_code == '':
            res = ConstData.msg_forbidden
            dlog.debug(res)
            self.write(res)
            return

        dlog.debug('wx_code:%s,wx_state:%s' % (wx_code, wx_state))

        access_token_res = wx_webapp.get_auth_access_token(code=wx_code, state=wx_state)
        user_info = wx_webapp.get_auth_user_info(auth_access_token_res=access_token_res)
        """:type:WeChatUser"""  # 微信返回的用戶信息串

        if user_info is None:
            res = ConstData.msg_forbidden
            dlog.debug(res)
            self.write(res)
            return

        wechat_user = await MisWeChatUser.objects.get(openid=user_info.openid, unionid=user_info.unionid)
        """:type:MisWeChatUser"""
        # 一個Open_id下面全部的id都是靠union來區分帳號

        if wechat_user is not None:
            user = await User.objects.get(user_id=wechat_user.user_id)
            assert user is not None
            if user.active:
                if await user.is_online():
                    await self.update_session()  # 更新時間
                else:
                    await self.create_session(user)  # 新增長一個session
                self.write('in authorized page')
                # self.redirect('/') # todo 重定向到登陸受權後的主頁
                return

        # 若是不存在wechat備案信息,則須要備案wechat信息,並且新註冊初始帳號
        default_new_user_id = 'u_' + get_uuid1_key()

        new_wechat_user = MisWeChatUser(
            openid=user_info.openid,
            nickname=user_info.nickname,
            unionid=user_info.unionid,
            # user_id=wx_webapp.appid + '_' + user_info.unionid, # 經過微信號登陸生成的一個惟一的用戶名,後面能夠提供修改
            user_id=default_new_user_id,
            appid=wx_webapp.appid
        )
        new_wechat_user.set_default_rc_tag()

        # rand_salt = get_rand_salt()
        new_user = User(
            user_id=default_new_user_id,
            # salt=rand_salt, # 防止別人md5撞庫反向破解的隨機數
            # passwd=StringField() # 密碼,經過第三方登陸的默認不設置
            first_name=user_info.nickname,
            status=FieldDict.user_status_init,  # 表示是可更改狀態
            active=False,
        )
        new_user.set_default_rc_tag()

        await new_wechat_user.save()
        await new_user.save()
        self.write('in unauthorized page')

        # self.redirect(URL_ROOT) # todo 導入到未受權的頁面
複製代碼

6 功能測試

設計兩組測試用例。

檢查微信用戶掃碼後可否完成上述流程:

  1. 用A微信帳號掃碼登陸,查看是否自動註冊
  2. 是否提示重定向到 「未受權頁面」

在數據庫中修改A微信自動註冊的用戶狀態爲審覈經過後再掃碼登陸:

  1. 修改A用戶狀態爲 active=True
  2. 是否提示重定向到 「受權頁面」
  3. 是否在數據庫中看到登陸的session狀態

測試截圖以下:

img

7 小結

若是我是一個產品經理,若是我作一個web應用的產品,那麼在產品早期階段,我確定會選擇微信登陸的方式,由於這種方式的登陸門檻實在是過低了,用戶試用產品的門檻也降到了最低,後續的活躍程度至少不會受到登陸的門檻的影響。

相關文章
相關標籤/搜索