钉钉二次开发分为如下表所示三种类型的开发,只有企业内部应用才需要对JSAPI鉴权。
类型 | 开发方式 | JSAPI鉴权 | 应用场景 |
第三方企业应用 | E应用开发 | 不需要 | 用于发布到钉钉应用市场,供广大用户下载,应用可选收费或免费,需要收取保证金,并进行应用审核 |
企业内部应用 | H5开发,8月中旬也开始支持E应用开发 | 需要 | 只能企业内部应用,不能发布到钉钉市场 |
第三方个人应用 | E应用开发 | 需要 | 应用与企业不挂钩,服务于个人 |
先来看一下钉钉官网给出的JSAPI鉴权的流程,它分为四个步骤:
- 获取token
- 获取ticket
- 获取数字签名
- 设置权限
查看钉钉开发文档,它给出了两个JSAPI的鉴权demo,分别是java和php的,需要前后端配合,而本文讲述的是纯前端JSAPI鉴权,完全实现前后端分离。
下面的代码将用到以下三个js依赖包:
import CryptoJS from 'crypto-js'; // 加密,用于生成数字签名import * as dd from "dingtalk-jsapi"; // 钉钉JSAPIimport axios from "axios"; // HTTP请求
提示:下面的代码,用了ES6和ES7的一些语法,没接触过的朋友,可自行百度
1、获取token
首先根据corpid和corpsecret参数,从钉钉服务器获取token。
/** * 获取token信息 * @param {Object} params {corpid, corpsecret} */async function getToken(params) { return await axios.get('/proxy/gettoken', { params }) .then(function(response) { return response.data.access_token; });}
细心的朋友会发现,axios请求的URL并没有直接指定 https://oapi.dingtalk.com/gettoken,这是因为直接写这个URL,会存在跨域问题,因此,这里我们用了代理。
代理可以采用nginx代理或者node代理,这里推荐用node代理。
因为需要代理的请求是https请求,本人也没跑通nginx代理https请求,如果有哪位朋友跑通了nginx代理的,麻烦告知一声,也学习以下。
2、设置代理
如果采用vue-cli创建的项目,可以直接在proxyTable中配置,如下:
proxyTable: { '/proxy': { target: 'https://oapi.dingtalk.com', secure: false, // 设置跨域 changeOrigin: true, pathRewrite: { '^/proxy': '' } } }
不过上面的配置只能在dev开发环境中使用,如果需要在生产环境中使用,还是需要自己写nodejs代码实现,如下:
var express = require('express');var proxy = require('http-proxy-middleware');var app = express();app.use('/proxy', proxy({ target: 'https://oapi.dingtalk.com', changeOrigin: true, pathRewrite: { '^/proxy': '' } }));app.listen(3000);
这代码量是不是很少,随便百度下都搞定。
3、获取ticket
根据上面获取的access_token,从钉钉服务器获取ticket。
/** * 获取ticket信息 * @param {Object} params {access_token} */async function getJsticket(params) { return await axios.get('/proxy/get_jsapi_ticket', { params }) .then(function(response) { return response.data.ticket; });}
4、获取数字签名
钉钉官网的demo是后台实现生成数字签名,通过crypto-js前端加密库就可以将这个功能移植到前端来。
ticket是上面生成的;
nonce可以是任何字符串;
timeStamp是当前时间戳;
url是当前访问的URL地址,不包括#及后面的部分,特别注意这个参数,别搞错了。如果设置错了,可以通过下面的dd.error输出查看钉钉服务器获取的URL地址
/** * 获取签名信息 * @param {*} ticket * @param {*} nonce * @param {*} timeStamp * @param {*} url */function getJsApiSingnature(ticket, nonce, timeStamp, url) { let plainTex = "jsapi_ticket=" + ticket + "&noncestr=" + nonce + "×tamp=" + timeStamp + "&url=" + url; let signature = CryptoJS.SHA1(plainTex).toString(); return signature;}
5、设置权限
通过dd.config设置需要的权限
// 步骤4:设置权限 dd.config({ agentId: agentId, corpId: corpid, //必填,企业ID timeStamp: timeStamp, // 必填,生成签名的时间戳 nonceStr: nonce, // 必填,生成签名的随机串 signature: signature, // 必填,签名 jsApiList: jsApiList // 必填,需要使用的jsapi列表,注意:不要带dd。 });
jsApiList是权限列表,如:
var jsApiList = [ 'biz.user.get', 'device.geolocation.get', 'biz.util.uploadImage'];
6、验证
上面配置成功之后,就能调用JSAPI中需要鉴权的功能了,下面以获取当前地理位置为例说明:
/** * 测试代码,通过调用位置服务,测试鉴权是否正确 */function testJsApi() { // 获取位置 dd.ready(() => { dd.device.geolocation.get({ targetAccuracy: 200, coordinate: 1, withReGeocode: false, useCache: true, //默认是true,如果需要频繁获取地理位置,请设置false, onSuccess: result => { /* 高德坐标 result 结构 { longitude : Number, latitude : Number, accuracy : Number, address : String, province : String, city : String, district : String, road : String, netType : String, operatorType : String, errorMessage : String, errorCode : Number, isWifiEnabled : Boolean, isGpsEnabled : Boolean, isFromMock : Boolean, provider : wifi|lbs|gps, accuracy : Number, isMobileEnabled : Boolean } */ console.log(result) alert("success: " + JSON.stringify(result)) }, onFail: err => { console.log(err) alert("error: " + JSON.stringify(err)) } }); }); // 查看鉴权错误信息 dd.error(function(err) { alert('dd error: ' + JSON.stringify(err)); });}
如果鉴权失败,则dd.error将输出钉钉服务器采用的数字签名参数,可以与自己采用的参数做比较
附:串起来的代码
async function getAccessToken() { // 步骤1:获取token let access_token = await getToken({ corpid, corpsecret }); // 步骤2:获取ticket let ticket = await getJsticket({ access_token }); // 步骤3:获取数字签名 let signature = getJsApiSingnature(ticket, nonce, timeStamp, url); // 步骤4:设置权限 dd.config({ agentId: agentId, corpId: corpid, //必填,企业ID timeStamp: timeStamp, // 必填,生成签名的时间戳 nonceStr: nonce, // 必填,生成签名的随机串 signature: signature, // 必填,签名 jsApiList: jsApiList // 必填,需要使用的jsapi列表,注意:不要带dd。 }); // 测试定位功能 testJsApi();}