公司對功能測試,性能測試,安全測試等等都作了比較好的自動化後,急須要一個MIS系統來統一管理這些結果及報表。python
此MIS系統特色以下:web
基於如上特色,顯然讓公司的人爲這樣一個內部系統而實現一個完整的帳號不太現實,要兼顧隱私性和便捷性的需求,做者想到了使用微信掃碼登陸來作爲身份認證,而後後臺管理員審覈,這樣就能夠達到以下的效果:數據庫
而後在開發完畢此係統後,以爲有必要再總結並且小升華一下,因而就有了本文的寫做動機。瀏覽器
原本本文的目的是 「實現微信掃碼登陸」 ,可是後來以爲僅僅是爲了實現這個功能,本文的立意又顯得過低。因此就在此處擴展一下爲 「多帳號受權登陸系統」 。安全
在近些年來,隨着互聯網愈來愈開發和協做,目前的系統登陸方式也愈來愈多,已經遠遠超越了之前的單用戶名的方式了。除了用戶名密碼外,通常網站還提供以下的登陸方式:服務器
第三方受權微信
微信/QQ/新浪微博(國內)Google/Facebook/Github(國外)session
綁定帳號app
手機號郵箱號dom
基於如上的幾種登陸方式,就造成了以下的 「多帳號登陸體系圖」:
基本原理:
第三方受權
可以從可信第三方獲取到相應的返回值(用戶信息),而後和 user_id 綁定不須要額外再輸入密碼便完成鑑權過程會建立一組從此能夠修改的 user_id 做爲 佔位用戶鑑權成功後設置session狀態
綁定帳號
事先已經完成了 user_id 的註冊完成了相應帳號綁定,即表示承認和 user_id 均能登陸使用和 user_id 一樣的或者不一樣的密碼體系(通常使用相同密碼)登陸完成鑑權鑑權成功後設置session狀態
關於 綁定帳號 的方式比較簡單,此處就再也不贅述。
基於 第三方受權 的方式,則比較精妙,可學習性比較強,由於基於互聯網愈來愈開放的特性,此方式確定會愈來愈多的被應用,愈來愈成爲主流。下面將以 微信掃碼 受權登陸爲例子來進行講解。
使用 微信掃碼 受權登陸的邏輯圖以下:
其中主要處理的事情以下:
使用過微信掃碼登陸系統的人會有以下的過程體驗(以著名社交網站 知乎 爲例子):
整個過程對於終端用戶來講,只有短短几秒,並且不用輸入任何密碼,能夠說是一種很是安全又便捷的體驗。
那麼問題來了,經過微信掃描二維碼,並完成MIS系統註冊登陸這個短短几秒的時間裏面,到底發生了哪些事情?
經過瀏覽器抓包,對幾個關鍵通信過程進行分析。
PC瀏覽器會依次發起兩個長鏈接(比較長時處於 pending 狀態)的請求:
這兩個狀態都會反饋到PC端的二維碼頁面,在手機端完成確認後,PC瀏覽器上面的頁面就會生定向到受權後的頁面(如 知乎 首頁)。
具體各方通信時序圖以下:
上圖對整個過程當中通信涉及的對象進行了清楚的描述,關於上圖數字標註部分註解以下:
關於微信掃碼認證部分的開發,本文再也不贅述,只給出以下注意事項:
根據如上原理,最後將提供具體實現代碼以供參考 ,爲了簡潔,有一些通用的工具函數的具體實現就不貼出來了。
使用 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 導入到未受權的頁面
複製代碼
設計兩組測試用例。
檢查微信用戶掃碼後可否完成上述流程:
在數據庫中修改A微信自動註冊的用戶狀態爲審覈經過後再掃碼登陸:
測試截圖以下:
若是我是一個產品經理,若是我作一個web應用的產品,那麼在產品早期階段,我確定會選擇微信登陸的方式,由於這種方式的登陸門檻實在是過低了,用戶試用產品的門檻也降到了最低,後續的活躍程度至少不會受到登陸的門檻的影響。