Asp.Net Core MVC 框架 實現釘釘掃碼登入


第一步:https://open-dev.dingtalk.com/  登入釘釘開放后台創建掃碼登錄應用授權

第二步:登錄界面前端二維碼展示:

前端頁面引入:

<script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>

 

在需要顯示二維碼的區域加一個DIV

<div id="login_container">

</div>

 下面代碼中涉及到的appid 和 appSecret是從創建掃碼登入應用授權中獲的。

在js腳本控制中加入代碼

 

                /*
                * 解釋一下goto參數,參考以下例子:
                * var url = encodeURIComponent('http://localhost.me/index.php?test=1&aa=2');
                * var goto = encodeURIComponent('https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=appid&response_type=code&scope=snsapi_login&state=STATE&redirect_uri='+url)
                */
                var url = encodeURIComponent('@ViewData["URL"]');
                var goto = encodeURIComponent('https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=dingoa4wipntvngzokythz&response_type=code&scope=snsapi_login&state=STATE&redirect_uri='+url)
        
                var obj = DDLogin({
                    id: "login_container",//這里需要你在自己的頁面定義一個HTML標簽並設置id,例如<div id="login_container"></div>或<span id="login_container"></span>
                    goto: goto, //請參考注釋里的方式
                    style: "border:none;background-color:rgba(128,128,128,0);color:#fff",
                    width: "300",
                    height: "300"
                });

                var handleMessage = function (event) {
                  var origin = event.origin;
                  console.log("origin", event.origin);
                  if(origin == "https://login.dingtalk.com" ) { //判斷是否來自ddLogin掃碼事件。
                      var loginTmpCode = event.data; 
                      layer.msg("掃碼登入中...", { icon: 6, shade: 0.1, anim: 5, time:1000 }, function () {
                        //獲取到loginTmpCode后就可以在這里構造跳轉鏈接進行跳轉了 get 參數 appid 是 從創建掃碼登錄應用授權中獲取,替換成自己創建的appid
                        window.location.href ="https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=appid&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=" +
                                url +
                                "&loginTmpCode=" +
                                loginTmpCode;
                       });
                      

                      
                  }
                };
                if (typeof window.addEventListener != 'undefined') {
                    window.addEventListener('message', handleMessage, false);
                } else if (typeof window.attachEvent != 'undefined') {
                    window.attachEvent('onmessage', handleMessage);
                }

 

解釋一下 @ViewData["URL"] 是怎么來的,這個是服務端獲取服務器 HOST 地址 然后返回到前端的,這樣是為了不寫死HOST地址。

服務端HOST的地址怎么地址如下:

[HttpGet]
public IActionResult UserLogin()
{
   string url = $"{this.Request.Scheme.ToString()}://{this.Request.Host.ToString()}/Login/DingDingLogin";
   ViewData["URL"] = url;
   return View();
}

 

第三步:掃碼后跳轉到服務端(/Login/DingDingLogin)是怎么處理的

        /// <summary>
        /// 釘釘掃碼登錄
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        [HttpGet]
        public IActionResult DingDingLogin(string code) 
        {
            MsgModel msgObj = null;
            string appId = _LoginAppId;
            string appSecret = _LoginAppSecret;
            string timestamp = CommonHelper.GetTimeStamp();//獲取時間戳
            string signature = CommonHelper.HmacSign(timestamp, appSecret);//根據時間戳和appSecret進行加密,參考釘釘開發文檔
            string url = $"https://oapi.dingtalk.com/sns/getuserinfo_bycode?accessKey={appId}&timestamp={timestamp}&signature={signature}";//參考釘釘開發文檔
            //前端掃碼后跳轉的url中可以獲取的code代碼,根據code代碼POST提交url可以獲取unionid

postData postData = new postData { tmp_auth_code = code };
 string res = JDRMYY.Utility.HttpRequest.https_post_request(url, JsonConvert.SerializeObject(postData),"POST","application/json");//自己封裝的一個服務端模擬POST提交方法 RequestUserInfo userInfo = JsonConvert.DeserializeObject<RequestUserInfo>(res); string unionId = userInfo.user_info.unionid; IDingTalkClient client_token = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken"); OapiGettokenRequest req_token = new OapiGettokenRequest(); req_token.SetHttpMethod("GET"); req_token.Appkey = _Appkey; req_token.Appsecret = _Appsecret; OapiGettokenResponse res_token = client_token.Execute(req_token); RequestTokenModel token = JsonConvert.DeserializeObject<RequestTokenModel>(res_token.Body); IDingTalkClient client_userid = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/getUseridByUnionid"); OapiUserGetUseridByUnionidRequest req_userid = new OapiUserGetUseridByUnionidRequest(); req_userid.Unionid = unionId; req_userid.SetHttpMethod("GET"); OapiUserGetUseridByUnionidResponse res_userid = client_userid.Execute(req_userid, token.access_token); RequestUserIdModel userid =  JsonConvert.DeserializeObject<RequestUserIdModel>(res_userid.Body); IDingTalkClient client_userinfo = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/get"); OapiUserGetRequest req_userinfo = new OapiUserGetRequest(); req_userinfo.Userid = userid.userid; req_userinfo.SetHttpMethod("GET"); OapiUserGetResponse res_userinfo = client_userinfo.Execute(req_userinfo, token.access_token); RequestUserModel user =  JsonConvert.DeserializeObject<RequestUserModel>(res_userinfo.Body);         //獲取到user.JobNumber(工號)和系統的內部在用的人員數據表進行比對,如果找到人員,則說明有權限登錄,否則則無法登錄系統(發送釘釘消息給用戶,並跳出錯誤頁面)。 string sql = "SELECT * FROM `employee` WHERE `empId` = ?empId and `useflag` = 1"; EmployeeModel LoginUser = _mysqlService.DBFind<EmployeeModel>(_qasystem, sql, new EmployeeModel() { empId = user.jobNumber }); if (LoginUser != null) { SetEmployeeRoles(LoginUser);//設置用戶權限 DI.WriteLogs(_mysqlService, _qasystem, new LogModel { log_content = "釘釘掃碼登入", log_time = DateTime.Now, log_writer = LoginUser.empName }); return Redirect("/Index/Index"); } else { string Content = $"**質量管理系統:** \n 您尚未注冊質量管理系統賬戶,請聯系**質量管理辦公室**注冊!"; SendDingTalkMsg(user.mobile, Content); DI.WriteLogs(_mysqlService, _qasystem, new LogModel { log_content = $"釘釘掃碼登入失敗,{LoginUser.empId},未注冊", log_time = DateTime.Now, log_writer = LoginUser.empName }); 
                return Redirect("/Login/LoginError"); } return View(); }

 這里面涉及到三個方法解釋下:

1、GetTimeStamp(獲取時間戳的方法)

2、HmacSign()(加密算法)

3、http_post_request() 服務端模擬POST提交方法

代碼:

/// <summary>
        /// Signature 簽名算法
        /// </summary>
        /// <param name="key"></param>
        /// <param name="text"></param>
        /// <returns></returns>
        public static string HmacSign(string message, string secret)
        {
            secret = secret ?? "";
            var encoding = new System.Text.ASCIIEncoding();
            byte[] keyByte = System.Text.Encoding.UTF8.GetBytes(secret);
            byte[] messageBytes = System.Text.Encoding.UTF8.GetBytes(message);
            using (var hmacsha256 = new HMACSHA256(keyByte))
            {
                byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
                return System.Web.HttpUtility.UrlEncode(Convert.ToBase64String(hashmessage));
            }
        }
        /// <summary>
        /// 獲取時間戳(毫秒)
        /// </summary>
        /// <returns></returns>
        public static string GetTimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalMilliseconds).ToString();
        }

 

 

public static string https_post_request(string host, string postdate, string method, string contenttype)
        {
            //格林威治時間
            DateTime dt = DateTime.UtcNow;
            //根據網站的編碼自定義
            Encoding encoding = Encoding.UTF8;
            //初始化一個HttpWebRequest請求實例
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(host);
            //獲取或設置cookie
            request.CookieContainer = new CookieContainer();
            //Http 請求方法 這里默認為POST
            request.Method = method;
            request.ServicePoint.Expect100Continue = false;
            //Http Post提交的數據的字節數
            request.ContentLength = Encoding.UTF8.GetByteCount(postdate);
            //Http 請求內容類型(一般為:application/xml)
            request.ContentType = contenttype;
            //Http 請求時間
            request.Date = dt;
            //POST數據字符串轉成字節
            byte[] postData = encoding.GetBytes(postdate);
            //post的內容長度
            request.ContentLength = postData.Length;
            //設置響應超時時間
            request.Timeout = 60000;
            string retString = "";
            StreamReader streamReader = StreamReader.Null;
            Stream responseStream = Stream.Null;
            HttpWebResponse response = null;

            //向內存中寫入POSTDATA數據流
            Stream requestStream = request.GetRequestStream();
            requestStream.Write(postData, 0, postData.Length);
            try
            {
                //獲取對應HTTP請求的響應
                response = (HttpWebResponse)request.GetResponse();
            }
            catch (WebException ex)
            {
                //獲取拋出異常的請求響應
                response = (HttpWebResponse)ex.Response;

            }
            responseStream = response.GetResponseStream();
            //如果http頭中接受gzip的話,這里就要判斷是否為有壓縮,有的話,直接解壓縮即可
            if (response.Headers["Content-Encoding"] != null && response.Headers["Content-Encoding"].ToLower().Contains("gzip"))
            {
                responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
            }

            streamReader = new StreamReader(responseStream, encoding);
            retString = streamReader.ReadToEnd();
            streamReader.Close();
            streamReader.Dispose();
            responseStream.Close();
            responseStream.Dispose();
            response.Close();
            return retString;
        }

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM