公司有兩個業務系統,A和B,AB用戶之間屬於多對一的關係,數據庫裏面也就是兩張表,A表有個外鍵指向B。如今須要實現如下幾個功能。前端
公司項目基於.net core 2.1 + Vue,git
後端有如下幾個子系統:github
前端有如下幾個系統,都是基於Vue的SPA:web
登陸這塊的邏輯實現方式是相似的。都是基於IdentityModel/oidc-client-js數據庫
簡單介紹一下IdentityServer這個東西。json
用戶登陸A或B系統,就是調用A和B對應的webapi,webapi配置了本身的驗證服務器是account服務器,account驗證未經過,前端就獲得401狀態碼,經過oidc-client-js的內部方法引導用戶進行登陸。跳轉account的頁面,用戶輸入用戶名密碼,登陸成功,account服務器判斷是A or B過來的登陸請求,帶上token回跳到配置的對應頁面。業務系統再次用獲取到的token請求api,調用成功。用一個圖來講。小程序
用了盛派微信sdk,特別感謝大佬的貢獻。c#
推薦一下微信沙箱環境,項目作完下來除了"沒法在測試的公衆號裏面推送小程序消息」沒法實現以外(由於推送的須要公衆號和小程序有一個綁定關係),其餘都ok。後端
由於版本的關係,account系統升級了asp.net core 2.2。api
這裏用到微信裏面生成帶參數的二維碼功能。B系統建立了用戶以後,生成一個對應的guid,而後把這個guid做爲參數,調用sdk就能獲得二維碼的url。
//建立ticket var qrRstTicketRst = await QrCodeApi.CreateAsync(weixinSetting.WeixinAppId, 30, 100000, QrCode_ActionName.QR_LIMIT_STR_SCENE, sceneId); //經過ticket獲取二維碼對應的url var url = QrCodeApi.GetShowQrCodeUrl(qrRstTicketRst.ticket);
這裏咱們項目中用到的是永久二維碼
,雖然這個二維碼上限10W個,咱們業務系統B用戶不會超過那麼多。
B用戶展現二維碼給A用戶,A用戶掃描,根據文檔:
若是用戶還未關注公衆號,則用戶能夠關注公衆號,關注後微信會將帶場景值關注事件推送給開發者。
若是用戶已經關注公衆號,在用戶掃描後會自動進入會話,微信也會將帶場景值掃描事件推送給開發者。
觸發代碼裏面分別對應的是OnEvent_SubscribeRequest
和OnEvent_ScanRequest
,兩個方法裏面的代碼基本上是同樣的。RequestMessageEvent_Scan.EventKey
能夠獲得上面的guid值。Subscribe事件裏面獲得的EventKey會比Scan的多一個qrscene
前綴,處理的時候要注意一點。兩個方法參數都能經過FromUserName
獲取到掃描的用戶的openId,而後在這個方法裏面返回一個帶參數(A的openId,和B的guid)的註冊連接,A用戶註冊的時候就提交了這兩個參數,後臺就能拿到。
順道說一句,公衆號裏面用戶每次操做只能被動返回一條消息。如要主動推送,須要用模板消息的方式。
對於老用戶,這裏須要一個帳號綁定的功能。也就是業務系統的帳號和openId作一個關聯。綁定的關鍵在於這個如何獲取這個openId,這裏有兩種方式。
OnEvent_ClickRequest
中,判斷RequestMessageEvent_Click.EventKey==xxx
,返回一個帶openId的綁定頁面的連接給用戶。好比/bind?openId=xxx,用戶點擊這個連接,系統引導用戶登陸,而後點擊綁定按鈕,實現綁定。系統中用戶和微信的openId已經綁定,因此,只要知道每次訪問頁面的openId就應該能實現自動登陸。openId是經過微信網頁受權的方式獲取到。流程能夠看文檔。簡單來講,先拿code,再換token,同時拿到openId。實現步驟分如下幾步。
{ "type": "view", "name": "登陸A", "url":"https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxb66259f2a353&redirect_uri=http%3A%2F%2Faccount.xxx.cn%2Fweixincallback%2Fcallback&response_type=code&scope=snsapi_base&state=spa.A#wechat_redirect" }
url中的state
參數會和code一塊兒返回給設置的redirectUrl,這個能夠用來給咱們在account登陸中心判斷是須要登陸A仍是B,以便最後回跳到對應的業務頁面。
這裏在沙箱配置跳轉域名的時候注意一下,只要寫域名就好。
callback 接收code和state兩個參數。
public async Task<IActionResult> Callback(string code, string state){...}//方法簽名
用這個code調用sdk裏面的api獲取token,同時能夠拿到openid。
var tokenResult = await OAuthApi.GetAccessTokenAsync(AppId, AppSecret, code); if (tokenResult.errcode != ReturnCode.請求成功) { throw new BizException("獲取微信用戶信息失敗"); } var openId = tokenResult.openid;
經過open獲取用戶信息。
var userInfo = await userService.GetByOpenId(openId);//userService是本身的業務service
而後調用HttpContext.SignInAsync
登陸。
public static async Task SignInAsync(this HttpContext context, string subject, string name, AuthenticationProperties properties, params Claim[] claims) { var clock = context.GetClock(); var user = new IdentityServerUser(subject) { DisplayName = name, AdditionalClaims = claims, AuthenticationTime = clock.UtcNow.UtcDateTime }; await context.SignInAsync(user, properties); }
HttpContext是當前請求的上下文。
subject能夠理解爲用戶的標識。
name能夠理解是用戶顯示的名字。
AuthenticationProperties是這次認證的一些配置,好比有效時長之類的。
Claim能夠理解爲這個subject帶的一些屬性。
await HttpContext.SignInAsync(userMobile, userName, props, claims);
調用完以後就登陸成功。
而後經過帶來的state參數判斷須要跳轉的client。
var client = await clientStore.FindClientByIdAsync(state); return Redirect($"{client.PostLogoutRedirectUris.FirstOrDefault()}?logined=true");
這裏帶一個logined=true參數,用來給client作一些邏輯。
首先要感謝的確定是盛派微信sdk的contributors,沒有他們系統對接起來應該會慢不少。
而後我想說,IdentityServer是個好東西,如今公司.NET相關的系統都已經用這個實現統一的登陸邏輯了,系統維護的代價小了許多。
提及來其實也是第一次對接微信公衆號相關的東西,在走通這條路以前走了很多彎路,不過好在走通了。但願對其餘人有幫助。