C#開發微信門戶及應用(41)--基於微信開放平臺的掃碼登陸處理

在現今不少網站裏面,都使用了微信開放平臺的掃碼登陸認證處理,這樣作至關於把身份認證交給較爲權威的第三方進行認證,在應用網站裏面能夠不須要存儲用戶的密碼了。本篇介紹如何基於微信開放平臺的掃碼進行網站的登錄處理。javascript

一、開放平臺的認證

要使用網站的掃碼登陸處理,就須要先進行微信開放平臺賬號的開發者資質認證,提交相關的資料,以及交付每一年300元的認證費用。html

認證後,創建相關的網站應用後,就有相關的APPID和APPSecret了,這些關鍵的參數就能夠用來獲取相關的用戶信息了。前端

網站應用的應用詳情界面以下所示。java

整個開放平臺感受沒有多少東西,不過須要收費認證才能使用這些功能,感受不是很爽。數據庫

咱們採用的掃碼登陸,須要經過開放平臺獲取用戶的信息,所以還須要設置獲取用戶基本信息接口的域名,不然沒法獲取信息,從而會致使重定向出錯。api

設置域名在【接口權限】【網頁帳號】【網頁受權獲取用戶基本信息】的修改入口,以下圖所示。安全

而後在彈出的對話框裏面輸入受權回調的域名便可。微信

 這樣設置就能夠確保獲取到用戶信息了。app

二、掃碼登陸的說明和具體使用

網站應用微信登陸是基於OAuth2.0協議標準構建的微信OAuth2.0受權登陸系統。函數

在進行微信OAuth2.在進行微信OAuth2.0受權登陸接入以前,在微信開放平臺註冊開發者賬號,並擁有一個已審覈經過的網站應用,並得到相應的AppID和AppSecret,申請微信登陸且經過審覈後,可開始接入流程。

微信OAuth2.0受權登陸讓微信用戶使用微信身份安全登陸第三方應用或網站,在微信用戶受權登陸已接入微信OAuth2.0的第三方應用後,第三方能夠獲取到用戶的接口調用憑證(access_token),經過access_token能夠進行微信開放平臺受權關係接口調用,從而可實現獲取微信用戶基本開放信息和幫助用戶實現基礎開放功能等。

微信OAuth2.0受權登陸目前支持authorization_code模式,適用於擁有server端的應用受權。該模式總體流程爲:

1. 第三方發起微信受權登陸請求,微信用戶容許受權第三方應用後,微信會拉起應用或重定向到第三方網站,而且帶上受權臨時票據code參數;

2. 經過code參數加上AppID和AppSecret等,經過API換取access_token;

3. 經過access_token進行接口調用,獲取用戶基本數據資源或幫助用戶實現基本操做。

獲取access_token時序圖:

 

從上圖咱們能夠大概瞭解整個掃碼登錄的處理過程。

 

三、掃碼登陸的各個步驟處理

1)用戶身份的綁定

爲了實現二維碼掃碼登陸,咱們須要在現有系統裏面綁定用戶的微信,這樣才能在用戶掃碼的時候,判斷用戶的身份從而實現自動登陸的過程。

咱們能夠在用戶管理裏面進行統一設置,也能夠在常規用戶登陸(用戶名+密碼)後進行設置,這個主要看咱們是否須要保留用戶名密碼登錄這種方式。

例如能夠在用戶管理裏面統一綁定,也就是在建立用戶的時候,讓用戶綁定下微信,獲取微信的惟一標識。

也能夠在保留用戶名密碼的登錄方式外,讓用戶登錄系統後自行進行綁定微信便可。

上面的界面,就是在一個頁面裏面彈出一個層,而後請求二維碼顯示便可,以下界面代碼所示。

複製代碼
        <div id="divWechat" class="easyui-dialog" style="width:450px;height:350px;padding:10px 20px"
             closed="true" resizable="true" modal="true" iconcls="icon-setting">
            <div>
                <h4>掃描用戶二維碼,進行綁定</h4>
            </div>
            <div align="center">
                <img id="imgQRcode" alt="使用微信掃碼進行綁定" style="height:200px;width:auto" />
            </div>

            <div align="right">
                <a href="javascript:void(0)" class="easyui-linkbutton" iconcls="icon-cancel" onclick="javascript: $('#divWechat').dialog('close')">關閉</a>
            </div>
        </div>
複製代碼

上面的層在打開的時候,咱們使用JS來動態獲取二維碼進行顯示,具體JS代碼以下所示。

複製代碼
    //綁定微信登錄
    function BindWechat() {
        var url = "http://www.iqidi.com/H5/BindWechat?id=@Session["UserID"]";
        url = encodeURIComponent(url);
        $("#imgQRcode").attr("src", "/H5/QR?url=" + url);
        //打開綁定窗口
        $("#divWechat").dialog('open').dialog('setTitle', '使用微信掃碼進行綁定');
    }
複製代碼

上面的JS只是作前端的數據請求和顯示,具體的QR動做Action其實就是生成掃描二維碼的過程,這個二維碼其實就是採用通用的方式,來構建一個指向咱們綁定帳號的地址,從而實現咱們綁定帳號的判斷,二維碼的生成過程以下所示。

複製代碼
        /// <summary>
        /// 轉換二維碼鏈接爲圖片格式
        /// </summary>
        /// <param name="url">二維碼鏈接</param>
        /// <returns></returns>
        [HttpGet]
        public ActionResult QR(string url)
        {
            //初始化二維碼生成工具
            QRCodeEncoder qrCodeEncoder = new QRCodeEncoder();
            qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE;
            qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M;
            qrCodeEncoder.QRCodeVersion = 0;
            qrCodeEncoder.QRCodeScale = 4;

            //將字符串生成二維碼圖片
            var image = qrCodeEncoder.Encode(url, Encoding.Default);
            //保存爲PNG到內存流  
            MemoryStream ms = new MemoryStream();
            image.Save(ms, ImageFormat.Png);
            image.Dispose();

            return File(ms.ToArray(), "image/Png");
        }
複製代碼

爲了實現用戶的綁定,咱們須要獲取當前用戶的身份信息,所以須要在BindWeChat的操做裏面作一個轉向處理,以下接口所示。

        /// <summary>
        /// 生成綁定微信的地址
        /// </summary>
        /// <returns></returns>
        public ActionResult BindWechat()

這個函數處理裏面,咱們須要從新定向處理,咱們把它定向到BindAccount函數裏面,方便獲取用戶的openid和其餘必要的信息。

另外咱們基於微信開放平臺的應用,創建了一個和微信帳號信息的聯繫,所以建立數據庫信息以下所示。

也就是一個具體的開放平臺應用對應着一個具體的微信帳號,這樣咱們就能夠充分利用配置進行處理了。

上面提到的BindAccount的處理的邏輯就是獲取必要的信息,而後在數據庫層面對身份信息進行驗證,具體代碼以下所示。

複製代碼
        /// <summary>
        /// 綁定用戶微信號
        /// </summary>
        /// <param name="id">帳號ID</param>
        /// <returns></returns>
        public ActionResult BindAccount()
        {
            WebAppInfo appInfo = GetWebApp(ConfigData.WebAppId);
            AccountInfo accountInfo = GetAccount(appInfo.AccountNo);

            var htResult = GetOpenIdAndUnionId(accountInfo.UniteAppId, accountInfo.UniteAppSecret);//存儲openid方便使用
            string openid = htResult["openid"].ToString();
            var unionid = htResult["unionid"].ToString();
            var userid = Request.QueryString["id"];
            var state = Request.QueryString["state"];

            if (!string.IsNullOrEmpty(openid) && !string.IsNullOrEmpty(userid))
            {
                CommonResult result = BLLFactory<User>.Instance.BindUser(openid, unionid, userid.ToInt32());
                if (result.Success)
                {
                    return BindSuccess();
                }
                else
                {
                    return BindFail();
                }
            }
            else
            {
                throw new WeixinException("沒法獲取openid" + string.Format(", openid:{0}, userid:{1}", openid, userid));
            }
        }
複製代碼

在綁定的過程,咱們須要考慮綁定正確帳號,重複綁定其餘帳號,無效綁定幾種狀況,若是成功綁定正確帳號(可屢次處理結果同樣),那麼獲得界面以下所示(這個界面的樣式採用了weui的樣式)。

  

 

2)用戶的掃碼登陸處理

上面綁定了帳號後,就能夠經過掃碼進行登陸了,掃碼回調的時候咱們有本身的判斷處理,掃碼界面以下所示(咱們在保留用戶名密碼登錄的方式外,增長了一個掃碼登陸的處理)。

若是是Bootstrap的界面效果

若是是EasyUI的界面效果

這個和前面的二維碼顯示規則差很少,不過他們的鏈接地址是不一樣的,這個地方用到了開放平臺的接口,也就是咱們前面提到開放平臺認證的接口了。

上面的掃碼登陸的界面代碼以下所示。

複製代碼
    <!--二維碼掃描登錄的界面層-->
    <div id="divWechat" class="easyui-dialog" style="width:550px;height:500px;padding:10px 20px"
         closed="true" resizable="true" modal="true" iconcls="icon-setting">
        <div id="login_container" align="center">
        </div>

        <div align="right">
            <a href="javascript:void(0)" class="easyui-linkbutton" iconcls="icon-cancel" onclick="javascript: $('#divWechat').dialog('close')">關閉</a>
        </div>
    </div>
複製代碼

上面代碼須要引入JS文件,並使用微信JSSDK的API進行顯示的。

複製代碼
    <!--使用微信掃碼進行登錄-->
    <script src="http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
    <script language="javascript">

        function OpenJSLogin() {
            var obj = new WxLogin({
                id: "login_container",
                appid: "@ViewBag.appid",
                scope: "snsapi_login",
                redirect_uri: "@ViewBag.redirect_uri",
                state: "@ViewBag.state",
                style: "black",
                href: ".impowerBox .qrcode {width: 200px;}"
            });

            //打開綁定窗口
            $("#divWechat").dialog('open').dialog('setTitle', '使用微信掃碼進行登錄');
        }
    </script>
複製代碼

這個裏面的參數,如APPID就是來源咱們認證後的開放平臺參數。

這些信息咱們在MVC控制器後面獲取後綁定在ViewBag,方便界面前端的使用。

複製代碼
            //使用JSLogin登錄
            WebAppInfo appInfo = BLLFactory<WebApp>.Instance.FindByID(ConfigData.WebAppId);
            ArgumentValidation.CheckForNullReference(appInfo, "Web應用程序appInfo");

            if (appInfo != null)
            {
                ViewBag.appid = appInfo.OpenAppID;
                ViewBag.redirect_uri = appInfo.LoginCallBackUrl;
                ViewBag.state = ConfigData.AuthState;
            }
複製代碼

其中的redirect_uri是經過數據庫獲取的LoginCallBackUrl地址,這個地址相似以下格式:http://www.iqidi.com/H5/callback?uid=iqidiSoftware

也就是咱們在開放平臺處理返回後進行的回調處理。

經過開放平臺的APPID和APPSecret,咱們能夠獲取到對應的接口調用憑證,而後根據接口憑證,以及openid,得到用戶的公衆平臺統一的UnionID,這個標識是咱們用戶的惟一標識,代碼以下所示。

複製代碼
                var result = baseApi.GetAuthToken(appid, appsecret, code);
                if (result != null && !string.IsNullOrEmpty(result.openid))
                {
                    openid = result.openid;
                    var unionResult = baseApi.GetSnsapiUserInfo(result.access_token, result.openid);

                    ht.Add("openid", openid);
                    ht.Add("unionid", unionResult != null ? unionResult.unionid : "");
                }
複製代碼

有了unionid咱們就能夠根據這個標識在咱們的用戶數據庫裏面查找對應的用戶,以下代碼所示。

            //開放平臺的OpenID,不是公衆號的OpenID,須要轉換爲unionid
            if (!string.IsNullOrEmpty(openid) && !string.IsNullOrEmpty(unionid))
            {
                UserInfo userInfo = BLLFactory<User>.Instance.FindByUnionId(unionid);

而後判斷咱們去到的用戶信息是否正確,以下代碼所示

複製代碼
                if (userInfo != null)
                {
                    CommonResult loginResult = CheckLogin(userInfo.Name);
                    if (!loginResult.Success)
                    {
                        LogHelper.Info(string.Format("用戶登錄不成功,{0}", loginResult.ErrorMessage));
                    }

                    //登錄成功後的重定向地址
                    var url = appInfo.HomeUrl;  //例如:http://www.iqidi.com/Home
                    return Redirect(url);
                }
複製代碼

若是不成功,那麼咱們定向到指定的界面便可。

            //如不成功,最後都統一提示信息
            ViewBag.Error = "獲取信息失敗,登錄錯誤";
            return View("LoginError");

若是咱們登錄成功後,須要設置一些Session信息或者Cookie信息,那麼就能夠經過CheckLogin函數進行處理便可。

以上就是咱們結合微信開放平臺實現微信掃碼登陸的過程,其中整個過程就是用到了下面幾個過程。

1)使用JSSDK的腳本實現掃碼獲取code

JS微信登陸主要用途:網站但願用戶在網站內就能完成登陸,無需跳轉到微信域下登陸後再返回,提高微信登陸的流暢性與成功率。 網站內嵌二維碼微信登陸JS實現辦法:

步驟1:在頁面中先引入以下JS文件(支持https):

<script src="http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>

步驟2:在須要使用微信登陸的地方實例如下JS對象:

複製代碼
                          var obj = new WxLogin({
                              id:"login_container", 
                              appid: "", 
                              scope: "", 
                              redirect_uri: "",
                              state: "",
                              style: "",
                              href: ""
                            });
複製代碼

2) 第二步:經過code獲取access_token

經過code獲取access_token

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

3)第三步:經過access_token調用接口

獲取access_token後,進行接口調用,

對於接口做用域(scope),能調用的接口有如下:

受權做用域(scope) 接口 接口說明
snsapi_base /sns/oauth2/access_token 經過code換取access_token、refresh_token和已受權scope
/sns/oauth2/refresh_token 刷新或續期access_token使用
/sns/auth 檢查access_token有效性
snsapi_userinfo /sns/userinfo 獲取用戶我的信息

其中snsapi_base屬於基礎接口,若應用已擁有其它scope權限,則默認擁有snsapi_base的權限。使用snsapi_base可讓移動端網頁受權繞過跳轉受權登陸頁請求用戶受權的動做,直接跳轉第三方網頁帶上受權臨時票據(code),但會使得用戶已受權做用域(scope)僅爲snsapi_base,從而致使沒法獲取到須要用戶受權才容許得到的數據和基礎功能。

4)獲取信息在回調界面中進行登陸前處理

經過上面接口,咱們能夠得到相應的用戶身份信息,所以能夠結合咱們用戶數據庫進行用戶身份的認定和處理,並設置必要的Session或者Cookie信息等,最後定位到咱們的應用主界面便可。

相關文章
相關標籤/搜索