钉钉扫码登录第三方网站
<main id="topic-2040858">
<p data-tag="shortdesc" id="shortdesc-mip-fut-h2q" class="shortdesc"><span style="color: rgba(17,31,44,0.40);font-size:14px;">更新时间:2022-02-24</span><br></p><div data-tag="taskbody" id="taskbody-3du-a6n-ny3" class="taskbody"><section id="section-qv2-w4z-gc9" class="section"><h2 data-tag="title" id="title-lm1-m6k-vku" class="title">简介</h2><p id="p-fxt-bhc-9v5">本教程指导你如何实现用户通过钉钉扫码直接登录到第三方网站。在本场景中,第三方网站可以获取在正在访问用户的钉钉身份无需用户输入账号密码。</p><h3 id="p-9st-nv4-3y3">准备工作</h3><p id="p-2t7-0ao-jt2">在开始本教程前,确保你已经完成了以下准备工作:</p><ul id="ul-we3-568-pog"><li id="li-ndq-zlq-xid"><p id="p-dho-7fe-ihd">已经完成了钉钉开发者的注册与激活并拥有了子管理员和开发者权限。若尚未完成,请参考<a href="/document/app/become-a-dingtalk-developer#topic-2024337" id="a-yuj-492-q1p" data-tag="xref" baseurl="t2039522_v1_0_0.xdita" data-node="2728530" data-root="66689" class="xref" target="_blank">成为钉钉开发者</a>。</p></li><li id="li-jve-16z-hff"><p id="p-74e-wmm-dbk">已经安装了Java开发环境。若未安装,请访问<a href="https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html" id="a-u7p-9ms-yt1" data-tag="xref" baseurl="" class="xref" target="_blank">Oracle官网</a>下载。</p></li><li id="li-a1u-ofe-0mh"><p id="p-z24-2uh-e51">已经安装了Java项目构建工具Maven。若未安装,请访问<a href="http://maven.apache.org/index.html" id="a-cqx-m63-utr" data-tag="xref" baseurl="" class="xref" target="_blank">Apache Maven</a>下载。</p></li><li id="li-3xp-j2a-mrf"><p id="p-jt1-daq-u9e">已下载并安装了<b>IntelliJ IDEA</b>开发工具。若未安装,请访问<a href="https://www.jetbrains.com/idea/download/#section=windows" id="a-ufo-nne-ahx" data-tag="xref" baseurl="t2039522_v1_0_0.xdita" class="xref" target="_blank">IntelliJ IDEA</a>。</p></li></ul></section><section id="section-urz-xm3-js8" class="section"><h2 data-tag="title" id="title-kqv-ew9-w7o" class="title">创建应用</h2><p id="p-eof-kor-559">在本部分,你将在开发者后台创建一个H5微应用,并完成通讯录权限的配置,用于本企业用户登录。</p><ol id="ol-upa-2gb-npf"><li id="li-onj-z9z-5kj"><p id="p-vbt-1wc-1fw">登录<a href="https://open-dev.dingtalk.com/" id="a-m4o-2n8-7pa" data-tag="xref" baseurl="" class="xref" target="_blank">钉钉开发者后台</a>。</p><div type="note" id="note-wvr-wmr-nm6" class="note note-note"><div class="note-icon-wrapper"><i class="icon-note note note"></i></div><div class="noteContentSpan"><strong>说明 </strong><p id="p-h24-z5c-yid">只有管理员和子管理员可登录开发者后台。</p></div></div></li><li id="li-t8s-zkg-xon"><p id="p-n4l-1vw-o22">在<b>开发者后台</b>页面,选择<b>企业</b><b>内部开发</b>,然后单击<b>创建应用</b>。</p><p id="p-6zz-e3q-ljn"><img alt="p255125 " src="https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/2260270261/p260944.png" placement="break" id="img-ucw-ckl-cd9" class="image break"><img alt="" placement="break" id="img-25g-s26-rsj" class="image break"></p></li><li id="li-p5s-byl-y40"><p id="p-dxp-ncd-omj">在弹出的创建应用页面中填写基本信息,然后单击<b>确定创建</b>。</p><ul id="ul-dhi-iru-vzm"><li id="li-oqz-j4a-n5z"><p id="p-ett-9jo-mbt"><b>应用类型</b>:选择<b>H5微应用</b>。</p></li><li id="li-zjz-zfq-ziy"><p id="p-3uw-90i-x67"><b>开发方式</b>:选择<b>企业自助开发</b>。</p></li></ul><img alt="p238094 " src="https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/4419648161/p261026.png" placement="break" id="img-bst-oee-tja" class="image break"></li><li id="li-fo0-pg0-c6x"><p id="p-uvm-9zo-82h">应用创建完成后,在<b>基础信息</b>页面,复制应用的<b>AppKey</b>和<b>AppSecret</b>备用。</p><img alt="p238073 " src="https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/4419648161/p261028.png" placement="break" id="img-jaj-qjx-246" class="image break"></li><li id="li-mx7-xaz-4xw"><p id="p-lzu-acy-vp7">单击<b>开发管理</b>进入开发管理页面,然后单击<b>修改,</b>并根据以下内容配置开发信息。</p><ul id="ul-s1y-xao-9lh"><li id="li-s54-nll-itj"><p id="p-akm-rzi-1x8"><b>开发模式</b>:选择<b>开发应用</b>。</p></li><li id="li-ll3-kb7-usr"><p id="p-g4f-4k9-5u0"><b>服务器出口IP</b>:输入调用钉钉服务端API时使用的IP即企业服务器的公网IP,多个IP请以英文逗号","隔开,支持带一个*号通配符的IP格式。</p><p id="p-tv4-fx6-6gc">本教程设置为<code data-tag="code" class="code">127.0.0.1</code>。</p></li><li id="li-pbz-xco-waj"><p id="p-xia-gil-41i"><b>应用首页地址</b>:输入应用首页URL,在移动端工作台点击应用图标会跳转到此页面。可输入后端服务部署的服务器的IP或域名。例如:<code data-tag="code" class="code">http://公网IP:8080</code>。</p><p id="p-eod-kx8-31y">本教程设置为<code data-tag="code" class="code">https://ding-doc.dingtalk.com/</code>。</p><img alt="p235737" src="https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/4389648161/p261065.png" placement="break" id="img-qty-8l8-vj7" class="image break"></li></ul></li><li id="li-u8c-qwh-7x4"><p id="p-60w-uy8-gud">单击<b>权限管理</b>进入权限管理页面,然后根据以下配置添加接口调用权限。</p><ol id="ol-s1q-grt-s0d"><li id="li-a5x-x9j-nmz"><p id="p-1nk-lyv-2v7">权限范围选择<b>全部员工</b>,然后选择<b>通讯录管理</b>。</p></li><li id="li-c43-ev4-qjl"><p id="p-hv3-km0-lzf">选择<b>通讯录部门信息读权限</b>和<b>通讯录部门成员读权限</b>,然后单击<b>申请权限</b>。</p><img alt="p237933 " src="https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/2494887161/p260978.png" placement="break" id="img-8ux-1bz-24s" class="image break"></li></ol></li></ol></section><section id="section-w09-v3s-jex" class="section"><h2 data-tag="title" id="title-v7t-zqu-9uk" class="title">配置内网穿透</h2><p id="p-jqn-tq4-sq2">在本部分,你将使用钉钉内网穿透工具生成一个公网域名用于教程测试。</p><ol id="ol-0tp-hta-8ki"><li id="li-b41-ouk-ib5"><p id="p-jby-kwr-fbc">打开命令行工具,执行以下命令,下载内网穿透工具。</p><pre id="codeblock-rc4-524-nib" data-tag="codeblock" class="pre codeblock"><code class="hljs awk">git clone https:<span class="hljs-regexp">//gi</span>thub.com<span class="hljs-regexp">/open-dingtalk/</span>pierced.git</code><div class="code-copy" data-clipboard-text="git clone https://github.com/open-dingtalk/pierced.git"><span><svg t="1642752622185" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9733" width="18" height="18"><path d="M127.962074 767.742104C57.571556 767.742104 0 710.155378 0 639.78003V127.962074C0 57.579141 57.579141 0 127.962074 0h447.844504C646.181926 0 697.35917 51.18483 703.753481 121.560178v6.394311c0 25.592415-19.190519 51.18483-51.184829 51.18483-31.986726 0-44.782933-25.592415-44.782933-51.18483 0-12.796207-12.796207-25.592415-25.592415-31.986726H127.962074c-12.796207 0-25.592415 12.796207-31.986726 25.592415v518.219852c0 12.796207 12.796207 25.592415 25.592415 31.986726h70.375348c25.592415 0 51.18483 19.190519 51.18483 51.184829s-25.592415 44.790519-51.18483 44.790519h-63.981037zM895.689007 351.876741H447.844504c-12.796207 0-25.58483 12.796207-31.986726 25.592415v518.219851c0 12.803793 12.796207 25.6 25.592415 31.994312h454.2464c12.796207 0 25.592415-12.796207 31.986726-25.592415v-518.219852c0-12.796207-12.796207-25.592415-31.986726-31.994311z m0-95.967763c70.375348 0 127.954489 57.579141 127.954489 127.962074v511.825541c0 70.375348-57.579141 127.954489-127.962074 127.954488H447.852089c-70.375348 0-127.954489-57.579141-127.954489-127.962074V383.878637c0-70.382933 57.579141-127.962074 127.962074-127.962074h447.844504z" p-id="9734"></path></svg></span><div class="code-copy-tip">一键复制</div></div></pre></li><li id="li-uyv-0fk-hk9"><p id="p-w2r-a2l-hj2">执行以下命令,启动内网穿透。</p><ul id="ul-qps-bi1-kyl"><li id="li-w75-3pg-f5p"><p id="p-i38-bjy-sif">Windows执行以下命令:</p><div type="note" id="note-050-c9r-6vl" class="note note-note"><div class="note-icon-wrapper"><i class="icon-note note note"></i></div><div class="noteContentSpan"><strong>说明 </strong><p id="p-aj8-2ic-rhv">Windows需使用cmd工具打开命令行。</p></div></div><pre id="codeblock-njh-7t6-5y6" data-tag="codeblock" class="pre codeblock"><code class="hljs mipsasm">cd windows_64
ding -config=ding.cfg -subdomain=abcde 8080
MAC执行以下命令:
cd mac_64
chmod 777 ./ding
./ding -config=./ding.cfg -subdomain=abcde 8080
一键复制
启动内网穿透时,请不要直接使用abcde字符串,如果需要使用默认端口8080,需确保在同一内网环境下该字符串唯一且未重复使用,如果重复,启动后可能会出现访问端口不一致问题。
启动完客户端后,你访问http://abcde.vaiwan.cn/xxxxx
都会映射到http://127.0.0.1:8080/xxxxx
。
创建扫码登录
在本部分,你将在开发者后台创建扫码登录,并完成基础配置。
登录钉钉开发者后台。
在应用详情页,单击钉钉登录与分享,然后输入回调域名,再点击添加。
说明如果你还没有域名,可以先使用配置内网穿透时设置的服务端域名,请求地址设置为/login。
搭建后端服务
在本部分,你将使用spring.io快速搭建服务端项目。
使用浏览器访问https://start.spring.io/。
根据以下操作下载服务端代码。
配置项目信息。
Project:选择Maven项目。
Language:选择Java。
Packaging:选择Jar包
Java:本教程选择Java8,你可以根据自身需要选择相应的Java版本。
单击GENERATE生成项目文件及下载。
解压下载的项目文件,然后使用IntelliJ IDEA打开。
参考以下步骤,下载并导入钉钉Java SDK。
单击这里下载钉钉服务端Java SDK。
解压dingtalk-sdk-java.zip。
在IntelliJ IDEA工具的顶部菜单栏中,选择File > Project Structure。
选择Libraries,然后单击 + 号,在弹出的界面单击Java。
选中下载的SDK,然后依次单击OK > Apply完成导入。
打开
pom.xml
文件,添加如下依赖。<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.6</version>
</dependency>一键复制等待项目加载完成后,在
src/main/java/com/example/demo/
目录下新建一个类LoginController.java
。然后在
LoginController.java
中添加以下代码。修改
getToken()
方法中AppKey和AppSecret为步骤一H5微应用的AppKey和AppSecret。修改appid和appSecret为步骤三创建扫码登录时创建的appid和appSecret。
import com.alibaba.fastjson.JSONObject;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.request.OapiSnsGetuserinfoBycodeRequest;
import com.dingtalk.api.request.OapiUserGetbyunionidRequest;
import com.dingtalk.api.request.OapiV2UserGetRequest;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.dingtalk.api.response.OapiSnsGetuserinfoBycodeResponse;
import com.dingtalk.api.response.OapiUserGetbyunionidResponse;
import com.dingtalk.api.response.OapiV2UserGetResponse;
import com.taobao.api.ApiException;
import org.springframework.web.bind.annotation.*;@RestController
public class LoginController {@RequestMapping(value = <span class="hljs-string">"/login"</span>) <span class="hljs-keyword">public</span> <span class="hljs-keyword">String</span> login(@RequestBody(required = <span class="hljs-literal">false</span>) JSONObject json, @RequestParam(<span class="hljs-string">"code"</span>) <span class="hljs-keyword">String</span> code) { <span class="hljs-keyword">try</span> { <span class="hljs-comment">// 获取access_token,注意正式代码要有异常流处理</span> <span class="hljs-keyword">String</span> access_token = getToken(); <span class="hljs-comment">// 通过临时授权码获取授权用户的个人信息</span> DefaultDingTalkClient client2 = <span class="hljs-keyword">new</span> <span class="hljs-type">DefaultDingTalkClient</span>(<span class="hljs-string">"https://oapi.dingtalk.com/sns/getuserinfo_bycode"</span>); OapiSnsGetuserinfoBycodeRequest reqBycodeRequest = <span class="hljs-keyword">new</span> <span class="hljs-type">OapiSnsGetuserinfoBycodeRequest</span>(); <span class="hljs-comment">// 通过扫描二维码,跳转指定的redirect_uri后,向url中追加的code临时授权码</span> reqBycodeRequest.setTmpAuthCode(code); <span class="hljs-comment">// 修改appid和appSecret为步骤三创建扫码登录时创建的appid和appSecret</span> OapiSnsGetuserinfoBycodeResponse bycodeResponse = client2.execute(reqBycodeRequest, <span class="hljs-string">"appId"</span>, <span class="hljs-string">"appSecret"</span>); <span class="hljs-comment">// 根据unionid获取userid</span> <span class="hljs-keyword">String</span> unionid = bycodeResponse.getUserInfo().getUnionid(); DingTalkClient clientDingTalkClient = <span class="hljs-keyword">new</span> <span class="hljs-type">DefaultDingTalkClient</span>(<span class="hljs-string">"https://oapi.dingtalk.com/topapi/user/getbyunionid"</span>); OapiUserGetbyunionidRequest reqGetbyunionidRequest = <span class="hljs-keyword">new</span> <span class="hljs-type">OapiUserGetbyunionidRequest</span>(); reqGetbyunionidRequest.setUnionid(unionid); OapiUserGetbyunionidResponse oapiUserGetbyunionidResponse = clientDingTalkClient.execute(reqGetbyunionidRequest, access_token); <span class="hljs-keyword">if</span> (oapiUserGetbyunionidResponse.getErrcode() == <span class="hljs-number">60121</span>L) { <span class="hljs-keyword">return</span> bycodeResponse.getBody(); } <span class="hljs-comment">// 根据userId获取用户信息</span> <span class="hljs-keyword">String</span> userid = oapiUserGetbyunionidResponse.getResult().getUserid(); DingTalkClient clientDingTalkClient2 = <span class="hljs-keyword">new</span> <span class="hljs-type">DefaultDingTalkClient</span>( <span class="hljs-string">"https://oapi.dingtalk.com/topapi/v2/user/get"</span>); OapiV2UserGetRequest reqGetRequest = <span class="hljs-keyword">new</span> <span class="hljs-type">OapiV2UserGetRequest</span>(); reqGetRequest.setUserid(userid); reqGetRequest.setLanguage(<span class="hljs-string">"zh_CN"</span>); OapiV2UserGetResponse rspGetResponse = clientDingTalkClient2.execute(reqGetRequest, access_token); System.out.println(rspGetResponse.getBody()); <span class="hljs-keyword">return</span> rspGetResponse.getBody(); } <span class="hljs-keyword">catch</span> (ApiException e) { e.printStackTrace(); <span class="hljs-keyword">return</span> <span class="hljs-string">"-1"</span>; } } <span class="hljs-keyword">public</span> <span class="hljs-keyword">String</span> getToken() { <span class="hljs-keyword">try</span> { DefaultDingTalkClient client = <span class="hljs-keyword">new</span> <span class="hljs-type">DefaultDingTalkClient</span>(<span class="hljs-string">"https://oapi.dingtalk.com/gettoken"</span>); OapiGettokenRequest request = <span class="hljs-keyword">new</span> <span class="hljs-type">OapiGettokenRequest</span>(); <span class="hljs-comment">// 填写步骤一创建应用的Appkey</span> request.setAppkey(<span class="hljs-string">"AppKey"</span>); <span class="hljs-comment">// 填写步骤一创建应用的Appsecret</span> request.setAppsecret(<span class="hljs-string">"AppSecret"</span>); request.setHttpMethod(<span class="hljs-string">"GET"</span>); OapiGettokenResponse response = client.execute(request); <span class="hljs-keyword">String</span> accessToken = response.getAccessToken(); <span class="hljs-keyword">return</span> accessToken; } <span class="hljs-keyword">catch</span> (ApiException e) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-type">RuntimeException</span>(); } }
}
<div class="code-copy" data-clipboard-text="import com.alibaba.fastjson.JSONObject;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiGettokenRequest;
import com.dingtalk.api.request.OapiSnsGetuserinfoBycodeRequest;
import com.dingtalk.api.request.OapiUserGetbyunionidRequest;
import com.dingtalk.api.request.OapiV2UserGetRequest;
import com.dingtalk.api.response.OapiGettokenResponse;
import com.dingtalk.api.response.OapiSnsGetuserinfoBycodeResponse;
import com.dingtalk.api.response.OapiUserGetbyunionidResponse;
import com.dingtalk.api.response.OapiV2UserGetResponse;
import com.taobao.api.ApiException;
import org.springframework.web.bind.annotation.*;@RestController
public class LoginController {@RequestMapping(value = "/login") public String login(@RequestBody(required = false) JSONObject json, @RequestParam("code") String code) { try { // 获取access_token,注意正式代码要有异常流处理 String access_token = getToken(); // 通过临时授权码获取授权用户的个人信息 DefaultDingTalkClient client2 = new DefaultDingTalkClient("https://oapi.dingtalk.com/sns/getuserinfo_bycode"); OapiSnsGetuserinfoBycodeRequest reqBycodeRequest = new OapiSnsGetuserinfoBycodeRequest(); // 通过扫描二维码,跳转指定的redirect_uri后,向url中追加的code临时授权码 reqBycodeRequest.setTmpAuthCode(code); // 修改appid和appSecret为步骤三创建扫码登录时创建的appid和appSecret OapiSnsGetuserinfoBycodeResponse bycodeResponse = client2.execute(reqBycodeRequest, "appId", "appSecret"); // 根据unionid获取userid String unionid = bycodeResponse.getUserInfo().getUnionid(); DingTalkClient clientDingTalkClient = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid"); OapiUserGetbyunionidRequest reqGetbyunionidRequest = new OapiUserGetbyunionidRequest(); reqGetbyunionidRequest.setUnionid(unionid); OapiUserGetbyunionidResponse oapiUserGetbyunionidResponse = clientDingTalkClient.execute(reqGetbyunionidRequest, access_token); if (oapiUserGetbyunionidResponse.getErrcode() == 60121L) { return bycodeResponse.getBody(); } // 根据userId获取用户信息 String userid = oapiUserGetbyunionidResponse.getResult().getUserid(); DingTalkClient clientDingTalkClient2 = new DefaultDingTalkClient( "https://oapi.dingtalk.com/topapi/v2/user/get"); OapiV2UserGetRequest reqGetRequest = new OapiV2UserGetRequest(); reqGetRequest.setUserid(userid); reqGetRequest.setLanguage("zh_CN"); OapiV2UserGetResponse rspGetResponse = clientDingTalkClient2.execute(reqGetRequest, access_token); System.out.println(rspGetResponse.getBody()); return rspGetResponse.getBody(); } catch (ApiException e) { e.printStackTrace(); return "-1"; } } public String getToken() { try { DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken"); OapiGettokenRequest request = new OapiGettokenRequest(); // 填写步骤一创建应用的Appkey request.setAppkey("AppKey"); // 填写步骤一创建应用的Appsecret request.setAppsecret("AppSecret"); request.setHttpMethod("GET"); OapiGettokenResponse response = client.execute(request); String accessToken = response.getAccessToken(); return accessToken; } catch (ApiException e) { throw new RuntimeException(); } }
}">
一键复制
构造扫码登录请求
运行
DemoApplication.java
,启动后端服务。打开浏览器,访问以下地址。
替换参数appid和redirect_uri为创建扫码登录时登录应用的appId和回调域名。
https://oapi.dingtalk.com/connect/qrconnect?appid=APPID&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=REDIRECT_URI
一键复制参数
是否必填
说明
appid
是
创建的登录应用的appid。
response_type
是
固定值为code。
scope
是
取值为snsapi_login用于钉钉容器外获取用户授权。
state
是
用于防止重放攻击,开发者可以根据此信息来判断redirect_uri只能执行一次来避免重放攻击。
redirect_uri
是
创建扫码登录应用授权时填写的回调域名。
浏览器显示如下图。
说明如果你需要将钉钉登录二维码内嵌到自己页面中,可以参考扫码登录第三方网站步骤二中的方式二将钉钉登录二维码内嵌到自己页面中。
使用手机钉钉扫描浏览器中的二维码,扫描结果如下图所示。
单击登录网页版三方登陆测试后,浏览器返回信息如下。
扫码成员在本企业内。
扫码成员不在企业内,返回的数据格式如下。
{
"errcode": 0,
"errmsg": "ok",
"user_info": {
"nick": "杨XX",
"unionid": "E2Mmii4axxx",
"dingId": "\(:LWCP_v1:\)gtEp97Mxxxgg==",
"openid": "AzF8w80nfpiSFxxxxx",
"main_org_auth_high_level": true
}
}一键复制
恭喜
你已完成本教程的全部内容!
你可以通过修改访问链接的方式实现钉钉内免登第三方网站和使用钉钉账号登录第三方网站。
例如:
钉钉内免登第三方网站
https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=APPID&response_type=code&scope=snsapi_auth&state=STATE&redirect_uri=REDIRECT_URI
一键复制使用钉钉账号登录第三方网站
https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=APPID&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=REDIRECT_URI
一键复制