钉钉扫码登录前端页面(附加参考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/

赞赏

微信赞赏支付宝赞赏

宝塔面板安装java-web环境
路由器url域名关键字过滤
pdo-namespace-error