钉钉扫码登录前端页面(附加参考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
";
$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/
赞赏
微信赞赏
支付宝赞赏
发表评论