钉钉扫码登录前端页面(附加参考php后端&钉钉消息通知)
钉钉扫码登录前端页面,另提供php后台处理授权code的参考代码。
http://dingding.ranjuan.cn/saoma.html——直接访问该地址,可以显示扫码
http://dingding.ranjuan.cn/getUserCode.html——上一步扫码后跳转的地址
使用前先在钉钉开发者后台配置扫码登录应用。回调域名我测试的时候,只要填写到顶级域名就行(如:http://dingding.ranjuan.cn),不一定非得填写到具体的uri路径
下面是saoma.html文件源码,注意修改redirect_uri与appid的配置
<head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script> <style> body{ background: url(./bg1.jpg) no-repeat 0px 0px; font-family: 'Open Sans', sans-serif; background-size:cover; -webkit-background-size:cover; -moz-background-size:cover; -o-background-size:cover; min-height:1050px; } h1 { font-family: 'Exo 2', sans-serif; text-align: center; padding-top: 4em; font-weight: 400; color: #2B2B36; font-size: 2em; } </style> <div class="main" style="margin 0 auto;text-algin:center;"><h1>统一登录平台</h1> <div id="login_container" style="margin:0 auto;width:370px;height:500px;"></div> <div> <script> var redirect_uri='http://dingding.ranjuan.cn/cms/getUserCode.html' var appid = 'dingoaii1csc1wtn' var state_time = Date.parse(new Date()); redirect_uri = encodeURIComponent(redirect_uri+'?test=test1'); //var goto = encodeURIComponent('https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid='+appid+'&response_type=code&scope=snsapi_login&state='+state_time+'&redirect_uri='+url) var goto = encodeURIComponent('https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid='+appid+'&response_type=code&scope=snsapi_login&state='+state_time+'&redirect_uri='+ redirect_uri) 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:#FFFFFF;", width : "365", height: "400" }); var handleMessage = function (event) {// 客户端扫码确认后触发获取loginTmpCode var origin = event.origin; console.log("origin", event.origin); if( origin == "https://login.dingtalk.com" ) { //判断是否来自ddLogin扫码事件。 var loginTmpCode = event.data; //拿到loginTmpCode后就可以在这里构造跳转链接进行跳转了 //var redirect_uri=redirect_uri; console.log("loginTmpCode", loginTmpCode); // alert( loginTmpCode); //window.location='https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid='+appid+'&response_type=code&scope=snsapi_login&state='+state_time+'&redirect_uri='+ redirect_uri+'&loginTmpCode='+loginTmpCode; window.location='https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid='+appid+'&response_type=code&scope=snsapi_login&state='+state_time+'&loginTmpCode='+loginTmpCode; //window.location=url_ding // 此链接处理成功后,会302跳转到你goto参数指定的redirect_uri,并向url参数中追加临时授权码code及state两个参数。 } }; if (typeof window.addEventListener != 'undefined') { window.addEventListener('message', handleMessage, false); } else if (typeof window.attachEvent != 'undefined') { window.attachEvent('onmessage', handleMessage); } </script>
下面是getUserCode.html文件源码,也就是上面saoma.html里面的redirect_uri配置(理论上来讲这个html页面逻辑可以交给后端一并处理)。其实这个html主要作用是把钉钉回调后带过来的code参数,交给后端进行用户信息的获取。后端根据接口只能获取到unionid,如果想要获取到用户的userid还需要进一步的接口调用,会需要用到企业微应用的appid和appkey(这个跟扫码登录创建的appid和appkey不是同一个东西哦)
<head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> </head> <script> function getUrlParam(name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); //构造一个含有目标参数的正则表达式对象 var r = window.location.search.substr(1).match(reg); //匹配目标参数 if (r != null) return decodeURI(r[2]); return null; //返回参数值 } alert('需要后端服务器请求钉钉的code:'+getUrlParam('code')); alert('前端ajax请求后台http://dingding.ranjuan.cn/api/DingLogin?code='+getUrlParam('code')+',等待返回code对应的用户的登录信息') // 将浏览器获取的code参数发送后台,由后台接口请求钉钉服务器接口,获取该code对应的unionid alert('后端根据code调用钉钉接口(获取unionid)https://oapi.dingtalk.com/sns/getuserinfo_bycode') // 后台根据unionid再去获取该unionid对应钉钉的userid /* {"errcode":0,"errmsg":"ok","user_info":{"nick":"\u5170\u5f3a","unionid":"hh0LIRUoydeY09l3ou","dingId":"$:LWCP_v1:$\/KVBpujmTsD+gK4VvTnn7g==","openid":"UmDpw5YNRSuDiPjs6Bkdrj","main_org_auth_high_level":true}} nick用户在钉钉上面的昵称 openid用户在当前开放应用内的唯一标识 unionid用户在当前开放应用所属企业内的唯一标识 */ alert('后端根据union调用钉钉接口(获取userid)https://oapi.dingtalk.com/user/getUseridByUnionid') alert('后端根据userid查找用户信息,返回前端成功登陆的用户信息') //前端将token等信息存入浏览器,跳转到登陆后的首页 // htmlobj=$.ajax({url:"http://dingding.ranjuan.cn/api/DingLogin",async:false}); // alert('后端接口返回'+htmlobj.responseText); // console.log(htmlobj.responseText); // 1.创建ajax对象 var xhr = new XMLHttpRequest(); // 2.告诉Ajax对象要向哪发送请求,以什么方式发送请求 // 1)请求方式 2)请求地址 xhr.open('get', 'http://dingding.ranjuan.cn/api/DingLogin?code='+getUrlParam('code')); // 3.发送请求 xhr.send(); // 4.获取服务器端响应到客户端的数据 xhr.onload = function (){ // xhr.status 获取http状态码 console.log(xhr.responseText); alert('后端接口返回'+htmlobj.responseText); if (xhr.status == 400) { alert('请求出错') } } // 当网络中断时会触发onerrr事件 xhr.onerror = function () { alert('错误404?? 跨域??') } </script>
用于参考的php后端。里面用到了钉钉的phpsdk(TopSdk.php钉钉SDK)。里面也包含了用于发送钉钉通知的接口demo,当时写测试用例的时候也尝试了几种不同类型的消息通知方式,可以看下面的$mms开头的数组变量做下参考!
<?php // 指定允许其他域名访问 header('Access-Control-Allow-Origin:*'); // 响应类型 header('Access-Control-Allow-Methods:POST'); // 响应头设置 header('Access-Control-Allow-Headers:x-requested-with,content-type'); error_reporting(E_ALL^E_NOTICE^E_WARNING);// 屏蔽报错代码不影响正常使用 $tmpcode=$_GET['code']; //echo '<br>获取到tmpcode'.$tmpcode; $appid='dingoaii1csc1wtn';//扫码的 $appsecret='8vmhlktUs12tfoSHQDI3i0Ds8-LOoSB18TsPLCFnq2hMUAFtmnjKz'; $appkey_qiye='dingcsf4irjarnz';// 用来定位企业内部应用-----方便获取到该钉钉用户在企业内的userid $appsecret_qiye='0lGFIvW42UaYx6H5EVRgvlFS1fvwBAbmbPK6_N7gv9T_Mq_2nJ6whGr0Xb'; $agentId='294775567';// 用来发送通知的应用app的agentid //配置结束 //////////////////获取accesstoken $url='https://oapi.dingtalk.com/gettoken?appkey='.$appkey_qiye.'&appsecret='.$appsecret_qiye; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); //return the transfer as a string curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // $output contains the output string $result = curl_exec($ch); //echo output // echo '<br><br>'.$result.'<br>'; // close curl resource to free up system resources curl_close($ch); //echo '<br>----<br>'; //var_dump($result); $accessToken_array=json_decode($result,true); $accessToken=$accessToken_array['access_token']; //////////////////获取accesstoken //echo '<br>获取到accessToken'.$accessToken; //////////////////////////获取unionid include "../TopSdk.php"; $c = new DingTalkClient(DingTalkConstant::$CALL_TYPE_OAPI, DingTalkConstant::$METHOD_POST , DingTalkConstant::$FORMAT_JSON); $req = new OapiSnsGetuserinfoBycodeRequest; $req->setTmpAuthCode($tmpcode); $resp=$c->executeWithAccessKey($req, "https://oapi.dingtalk.com/sns/getuserinfo_bycode",$appid,$appsecret); //echo '<br>'; /* nick用户在钉钉上面的昵称 openid用户在当前开放应用内的唯一标识 unionid用户在当前开放应用所属企业内的唯一标识 */ //$resp=json_encode($resp); //echo '<br>获取到unionid'; //var_dump($resp); //echo '<br><br><br>'; $unionid=$resp->user_info->unionid; //////////////////////////获取unionid //////////////////////unionid 与accesstoken 获取userid 注意accesstoken获取的appkey 与appsecret 同web扫码登录的appId appSecret //include('TopSdk.php'); $c = new DingTalkClient(DingTalkConstant::$CALL_TYPE_OAPI, DingTalkConstant::$METHOD_GET , DingTalkConstant::$FORMAT_JSON); $req = new OapiUserGetUseridByUnionidRequest; $req->setUnionid($unionid); $resp=$c->execute($req,$accessToken, "https://oapi.dingtalk.com/user/getUseridByUnionid"); //echo '<br><br>userid<br>'; //var_dump($resp); $userid=$resp->userid; /* { "errcode": 0, "errmsg": "ok", "contactType": 0, "userid": "userid" } */ //////////////////////unionid 与accesstoken 获取userid $userid->errcode==0?'':die('无该接口的调用权限'); //获取用户信息 //echo $accessToken; // DingTalkConstant::$METHOD_GET 要与下面调用接口url要求的保持一致 $c = new DingTalkClient(DingTalkConstant::$CALL_TYPE_OAPI, DingTalkConstant::$METHOD_GET , DingTalkConstant::$FORMAT_JSON); $req = new OapiUserGetRequest(); $req->setUserid($userid);// 用户 id $resp=$c->execute($req, $accessToken,"https://oapi.dingtalk.com/user/get"); $user_info=$resp; //echo '<br><br>用户返回信息:'; //var_dump($resp); //echo '<br>========================<br>'; $user_info->errcode==0?'':die('您不是本企业用户,无法登录系统'); /* echo '<br><img src="'.$user_info->avatar.'" style="width:100px;"/>'; echo '<br> 姓 名: '.$user_info->name; echo '<br>用户id: '.$user_info->userid; echo '<br>手机号: '.$user_info->mobile; echo '<br> 职 位: '.$user_info->position; */ ////////////发送消息 $c = new DingTalkClient(DingTalkConstant::$CALL_TYPE_OAPI, DingTalkConstant::$METHOD_POST , DingTalkConstant::$FORMAT_JSON); $req = new OapiMessageCorpconversationAsyncsendV2Request(); //$req->setUseridList($content_arr["userid"]);// 必填,与offset和limit配合使用 $req->setUseridList($userid);//userid_list $req->setAgentId($agentId); //$req->setToAllUser(false); //$mms='{"msgtype":"text","text":{"content":"消息内容"}}'; //$req->setMsg($mms); //$msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg(); //$msg->setMsgtype("text"); //$msg->setText(new OapiMessageCorpconversationAsyncsendV2Request.Text()); //$msg->getText().setContent("test123"); $mms1=array("msgtype"=>"text", "text"=>array("content"=>$user_info->name.',您好!您已通过钉钉扫码登录系统。 '.date("Y-m-d H:i:s")." &".rand(10000,99999)) ); $mms2['msgtype']='link'; $mms2['link']['messageUrl']='https://dingding.ranjuan.cn'; $mms2['link']['picUrl']='@lALOACZwe2Rk'; $mms2['link']['title']=$user_info->name.' 您已登录系统'; $mms2['link']['text']='点击查看详情~'.rand(1000,9999); $mms['msgtype']= 'oa'; //$mms["oa"]["message_url"]= "#"; $mms["oa"]["head"]["bgcolor"]= "FFBBBBBB"; $mms["oa"]["head"]["text"]= "系统登录";/////向普通会话发送时有效,向企业会话发送时会被替换为微应用的名字),长度限制为最多10个字符 $mms["oa"]['body']['title']=date("Y-m-d H:i:s").'登录推送'; $mms["oa"]['body']['form'][0]['key']='姓名:'; $mms["oa"]['body']['form'][0]['value']=$user_info->name; $mms["oa"]['body']['form'][1]['key']='用户id:'; $mms["oa"]['body']['form'][1]['value']=$user_info->userid; //$mms["oa"]['body']['rich']['num']='抽奖码:'.rand(1111,8855);//富文本 //$mms["oa"]['body']['rich']['unit']='NO.'; $mms["oa"]['body']['content']='已登录系统'; $mms["oa"]['body']['image']='@lADOADmaWMzazQKA'; $mms["oa"]['body']['file_count']='0'; $mms["oa"]['body']['author']='系统消息'; $mms4["msgtype"]="markdown"; $mms4["markdown"]["title"]="首屏会话透出的展示内容"; $mms4["markdown"]["text"]= " # 这是支持markdown的文本 \n ## 标题2 \n ### 标题3333 \n * 列表1 \n * 列表12222 \n **bold加粗显示** \n *italic斜体显示* \n > A 外事问谷歌,内事问度娘 \n [this is a link](https://baidu.com) \n ![alt 啊](https://img.alicdn.com/tps/TB1XLjqNVXXXXc4XVXXXXXXXXXX-170-64.png)"; $mms5["msgtype"]= "action_card"; $mms5['action_card']['title']='店长/部门事务审批'; $mms5['action_card']['markdown']="### 标题3333 \n 周五 \n 部门三人 张三、赵四、王五 \n 一起去 南京奥体管 \n 参加荣誉企业颁奖典礼 \n [this is a link](https://baidu.com) \n"; $mms5['action_card']['btn_orientation']= "1"; $mms5['action_card']['btn_json_list'][0]['title']='拒绝'; $mms5['action_card']['btn_json_list'][0]['action_url']='https://www.baidu.com'; $mms5['action_card']['btn_json_list'][1]['title']='同意'; $mms5['action_card']['btn_json_list'][1]['action_url']='https://www.taobao.com'; //$mms=json_encode($mms,true); //echo '<br><br>'.$mms; //$mms=json_encode($mms); //$mms='{"msgtype":"text","text":{"content":"消息xxxxxxxxxxxxxxxxxxx内容"}}'; //$mms='{"msgtype":"text","text":{"content":"12消息内容"}}'; //$arrayobject = arrayToObject($mms); //$req->setMsg($arrayobject); $req->setMsg($mms); //$req->setMsg($mms);// 虽然报错 但是可以从发送消息 $resp=$c->execute($req, $accessToken,"https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2"); //echo '<br><br>消息发送状态'; //var_dump($resp); ////////////发送消息 /* echo '<br><br><br>登录消息发送状态: '; echo $resp->errcode==0?'[成功]':die('[失败]'); echo '<br>钉钉消息ID:'.$resp->task_id; echo '<br>request_id:'.$resp->request_id; */ session_start(); $_SESSION['userid']=$user_info->userid; $_SESSION['mobile']=$user_info->mobile; $_SESSION['name']=$user_info->name; $_SESSION['isadmin']=$user_info->isAdmin; $_SESSION['department']=$user_info->department; $logtxt='钉钉UserId:'.$_SESSION['userid'].' ' .'登录用户:'.$_SESSION['name'].' ' .' 部门列表:'.json_encode($_SESSION['department']).' '; file_put_contents("login.txt","\n ".date('Y-m-d H:i:s',time())." | 钉钉扫码登录平台 | http://".$_SERVER['SERVER_NAME'].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"].' | 来源:'.$_GET['referer']."\n ".$logtxt,FILE_APPEND); header('location:'.$_GET['referer']); die; echo ' <script> localStorage.removeItem("key");//删除数据 localStorage.setItem("key","user:'.$_SESSION['userid'].'-'.$_SESSION['userid'].'"); var data = localStorage.getItem("key");//获取数据 //localStorage.removeItem("key");//删除数据 //localStorage.clear();//删除保存的所有数据 alert(\'已登录,请重新选择操作菜单\'); window.location.href="http://dingding.ranjuan.cn/index.php"; </script> '; die;
有些钉钉后端调用接口可能需要将服务器ip加入钉钉开发者应用配置的ip出口白名单!
基于互联网精神,在注明出处的前提下本站文章可自由转载!
本文链接:https://ranjuan.cn/dingding-code-scanning-login/
赞赏
微信赞赏支付宝赞赏
发表评论