钉钉扫码登录前端页面,另提供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) {// 客户端扫码确认后触发获取loginTmpCodevar 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对应的unionidalert('后端根据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 stringcurl_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 resourcescurl_close($ch);//echo '<br>----<br>';//var_dump($result);$accessToken_array=json_decode($result,true);$accessToken=$accessToken_array['access_token'];//////////////////获取accesstoken//echo '<br>获取到accessToken'.$accessToken;//////////////////////////获取unionidinclude "../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";$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出口白名单!
发表评论