钉钉H5微应用Springboot+Vue开发分享
文章目录
- 说明
- 技术路线
- 注意
- 操作步骤
- 思路图
- 一、创建钉钉应用
- 二、创建java项目
- 三、创建vue项目(或uniapp项目),npm引入sdk的依赖
- 四、拥有公网域名端口。开发环境可以使用(贝锐花生壳等工具)
- 五、打开钉钉开发者平台,配置钉钉应用的h5公网地址
- 六、打开手机钉钉,即可看到开发的页面
说明
由于钉钉开发文档的内容特别多,虽然介绍已经非常仔细了,当对于那些第一次看这个文档的时候,会有些疑惑。为了避免少走很多弯路,故写下该文章进行技术分享
- 本文主要功能:1、钉钉免登录获取用户信息 2、钉钉获取当前的定位
简单来说,就是在钉钉里面,展示我们编写的手机格式大小的网页页面
技术路线
VUE作为前端开发框架,后端为Springboot项目
可以直接通过npm运行项目或者nginx运行项目
为了方便(只需要部署一个项目),我把vue打包成为静态文件,放置到Springboot的 static 文件中。
注意
1、钉钉开发文档,有时候叫 开发H5微应用,有时候叫 开发网页应用,注意分辨
2、开发过程中,有时候会用到小程序开发者工具,注意看说明书。jsapi接口有时候这个工具用不了,得实际放到钉钉dingtalk才有用
3、目前该分享,只是涉及到网页应用,不涉及小程序应用。要注意分辨
操作步骤
1、获取钉钉的应用(corpId/agentId/appKey/appSecret)。开发环境可以自己注册企业,自己创建钉钉应用(注意配置免密的权限)
2、创建java项目,pom引入钉钉的sdk
3、创建vue项目(或uniapp项目),npm引入sdk的依赖
4、拥有公网域名端口。开发环境可以使用(贝锐花生壳等工具)
5、打开钉钉开发者平台,配置钉钉应用的h5公网地址
6、打开手机钉钉,即可看到开发的页面
思路图
获取免登录
jsapi鉴权获取定位坐标(只有安卓端 或 苹果端有用)
一、创建钉钉应用
注册钉钉企业,打开钉钉开发者平台
https://open-dev.dingtalk.com/
记录下 corpId
创建应用
记录下 agentId、appKey、appSecret
二、创建java项目
POM引入依赖,因为钉钉的接口分为新的接口和旧的接口,目前最新的版本,新接口和旧接口都是可以使用的。所以两个接口的依赖同时引入
参考我上传到 gitee的后端代码
https://gitee.com/chencanzhan/cancan-java-share/tree/master/dingtalk-demo
核心pom文件
<!-- 新的接口 --><dependency><groupId>com.aliyun</groupId><artifactId>dingtalk</artifactId><version>2.1.21</version></dependency><!-- 旧的接口 --><dependency><groupId>com.aliyun</groupId><artifactId>alibaba-dingtalk-service-sdk</artifactId><version>2.0.0</version></dependency>
核心代码
@Service
public class DingH5Service {@Value("${dingtalk.appKey}")private String appKey;@Value("${dingtalk.appSecret}")private String accessKeySecret;public DingUserInfo getUserByCode(String code) {DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/getuserinfo");OapiV2UserGetuserinfoRequest req = new OapiV2UserGetuserinfoRequest();req.setCode(code);OapiV2UserGetuserinfoResponse rsp = null;try {rsp = client.execute(req, getAccessToken());} catch (Exception e) {throw new RuntimeException(e);}cn.hutool.json.JSONObject entries = JSONUtil.parseObj(rsp.getBody());Integer errcode = entries.getInt("errcode");if(errcode == 0){cn.hutool.json.JSONObject result = entries.getJSONObject("result");DingUserInfo dingUserInfo = new DingUserInfo();dingUserInfo.setAssociatedUnionid(result.getStr("associated_unionid"));String unionid = result.getStr("unionid");dingUserInfo.setUnionid(unionid);String deviceId = result.getStr("device_id");dingUserInfo.setDeviceId(deviceId);dingUserInfo.setSysLevel(result.getInt("sys_level"));String name = result.getStr("name");dingUserInfo.setName(name);dingUserInfo.setSys(result.getBool("sys"));String userid = result.getStr("userid");dingUserInfo.setUserid(userid);return dingUserInfo;}return null;}public String getJsapiTicket() {com.aliyun.dingtalkoauth2_1_0.Client client = null;try {client = createClient();} catch (Exception e) {throw new RuntimeException(e);}try {com.aliyun.dingtalkoauth2_1_0.models.CreateJsapiTicketHeaders createJsapiTicketHeaders = new com.aliyun.dingtalkoauth2_1_0.models.CreateJsapiTicketHeaders();createJsapiTicketHeaders.xAcsDingtalkAccessToken = getAccessToken();CreateJsapiTicketResponse jsapiTicketWithOptions = client.createJsapiTicketWithOptions(createJsapiTicketHeaders, new RuntimeOptions());CreateJsapiTicketResponseBody body = jsapiTicketWithOptions.getBody();return body.getJsapiTicket();} catch (TeaException err) {if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {// err 中含有 code 和 message 属性,可帮助开发定位问题}} catch (Exception _err) {TeaException err = new TeaException(_err.getMessage(), _err);if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {// err 中含有 code 和 message 属性,可帮助开发定位问题}}return null;}/*** 创建钉钉客户端* @return* @throws Exception*/public static com.aliyun.dingtalkoauth2_1_0.Client createClient() throws Exception {com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();config.protocol = "https";config.regionId = "central";return new com.aliyun.dingtalkoauth2_1_0.Client(config);}public String getAccessToken() throws Exception {com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();config.protocol = "https";config.regionId = "central";com.aliyun.dingtalkoauth2_1_0.Client client = new com.aliyun.dingtalkoauth2_1_0.Client(config);com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest getAccessTokenRequest = new com.aliyun.dingtalkoauth2_1_0.models.GetAccessTokenRequest().setAppKey(appKey).setAppSecret(accessKeySecret);try {return client.getAccessToken(getAccessTokenRequest).getBody().getAccessToken();} catch (TeaException err) {if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {// err 中含有 code 和 message 属性,可帮助开发定位问题}} catch (Exception _err) {TeaException err = new TeaException(_err.getMessage(), _err);if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {// err 中含有 code 和 message 属性,可帮助开发定位问题}}return null;}}
@RestController
@RequestMapping("/ding-h5")
public class DingH5Controller {@Value("${dingtalk.agentId}")private String agentId;@Value("${dingtalk.corpId}")private String corpId;@Value("${dingtalk.appKey}")private String appKey;@Value("${dingtalk.urlPath}")private String urlPath;@Autowiredprivate DingH5Service dingH5Service;/*** 获取签名* @param dingConfigSignVo* @param request* @return*/@PostMapping("/signAll")public ResponseEntity<Object> signAll(@RequestBody DingConfigSignVo dingConfigSignVo, HttpServletRequest request){String sign = null;String signedUrl = urlPath;String jticket = dingH5Service.getJsapiTicket();dingConfigSignVo.setJsticket(jticket);Map<String, Object> jMap = new HashMap<>();try {sign = DdConfigSign.sign(dingConfigSignVo.getJsticket(),dingConfigSignVo.getNonceStr(),dingConfigSignVo.getTimeStamp(),signedUrl);} catch (Exception e) {throw new RuntimeException(e);}jMap.put("agentId",agentId);jMap.put("corpId",corpId);jMap.put("appKey",appKey);jMap.put("sign",sign);return new ResponseEntity<>(jMap, HttpStatus.OK);}/*** 根据code获取用户信息* @param code* @return*/@GetMapping("/getUserByCode")public ResponseEntity<Object> getUserByCode(String code){DingUserInfo userByCode = dingH5Service.getUserByCode(code);return new ResponseEntity<>(userByCode, HttpStatus.OK);}}
/*** 计算dd.config的签名参数**/
public class DdConfigSign {/*** 计算dd.config的签名参数** @param jsticket 通过微应用appKey获取的jsticket* @param nonceStr 自定义固定字符串* @param timeStamp 当前时间戳* @param url 调用dd.config的当前页面URL* @return* @throws Exception*/public static String sign(String jsticket, String nonceStr, long timeStamp, String url) throws Exception {String plain = "jsapi_ticket=" + jsticket + "&noncestr=" + nonceStr + "×tamp=" + String.valueOf(timeStamp)+ "&url=" + decodeUrl(url);try {MessageDigest sha1 = MessageDigest.getInstance("SHA-256");sha1.reset();sha1.update(plain.getBytes("UTF-8"));return byteToHex(sha1.digest());} catch (Exception e) {throw new Exception(e.getMessage());}}// 字节数组转化成十六进制字符串private static String byteToHex(final byte[] hash) {Formatter formatter = new Formatter();for (byte b : hash) {formatter.format("%02x", b);}String result = formatter.toString();formatter.close();return result;}/*** 因为ios端上传递的url是encode过的,android是原始的url。开发者使用的也是原始url,* 所以需要把参数进行一般urlDecode** @param url* @return* @throws Exception*/private static String decodeUrl(String url) throws Exception {URL urler = new URL(url);StringBuilder urlBuffer = new StringBuilder();urlBuffer.append(urler.getProtocol());urlBuffer.append(":");if (urler.getAuthority() != null && urler.getAuthority().length() > 0) {urlBuffer.append("//");urlBuffer.append(urler.getAuthority());}if (urler.getPath() != null) {urlBuffer.append(urler.getPath());}if (urler.getQuery() != null) {urlBuffer.append('?');urlBuffer.append(URLDecoder.decode(urler.getQuery(), "utf-8"));}return urlBuffer.toString();}public static String getRandomStr(int count) {String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < count; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}
}
三、创建vue项目(或uniapp项目),npm引入sdk的依赖
1、使用npm安装。
npm install dingtalk-jsapi --save2、加载 dingtalk-jsapi
import * as dd from 'dingtalk-jsapi'; // 此方式为整体加载,也可按需进行加载
完整的代码
<template><el-main><div>用户名:{{name}}</div><div>当前位置:{{rrsss.address}}</div><button @click="handlegetSignAll">测试</button></el-main>
</template><script>import api from '@/api';
import * as dd from 'dingtalk-jsapi'; // 此方式为整体加载,也可按需进行加载export default {data() {return {t1: 0,name: '',agentId: '',appKey: '',corpId: '',sign: '',rrsss: {},}},mounted() {this.handlegetSignAll();},methods: {handlegetSignAll() {this.t1 = Date.now()let params = {nonceStr: 'a',timeStamp: this.t1}api.getSignAll(params).then(res => {if (res && res.status === 200) {this.agentId = res.data.agentIdthis.appKey = res.data.appKeythis.corpId = res.data.corpIdthis.sign = res.data.signthis.setDDConfig();this.getAuthCode();}})},setDDConfig() {/**钉钉鉴权 */dd.config({agentId: this.agentId, // 必填,微应用IDcorpId: this.corpId,//必填,企业IDtimeStamp: this.t1, // 必填,生成签名的时间戳nonceStr: 'a', // 必填,自定义固定字符串。signature: this.sign, // 必填,签名type: 0, //选填。0表示微应用的jsapi,1表示服务窗的jsapi;不填默认为0。该参数从dingtalk.js的0.8.3版本开始支持jsApiList: ['device.geolocation.get'] // 必填,需要使用的jsapi列表,注意:不要带dd。})this.getGeolocation();//该方法必须带上,用来捕获鉴权出现的异常信息,否则不方便排查出现的问题dd.error(function () {console.log("钉钉鉴权失败,无法定位等,请联系管理员,或重新尝试!");})},getGeolocation() {dd.ready(() => {dd.device.geolocation.get({targetAccuracy: 200,coordinate: 1,withReGeocode: true,useCache: false,onSuccess: function (res) {// 调用成功时回调console.log(res)this.rrsss = res},onFail: function (err) {// 调用失败时回调console.log(err)}});})},getAuthCode() {dd.requestAuthCode({corpId: this.corpId,clientId: this.appKey,onSuccess: (result) => {api.getUserInfo({code:result.code}).then(res => {if (res && res.status === 200) {this.name = res.data.name}})},onFail: function () { },});} }
}
</script>
<style scoped>
</style>
四、拥有公网域名端口。开发环境可以使用(贝锐花生壳等工具)
这自己百度,映射到本地端口可以直接通过npm运行项目或者nginx运行项目
为了方便(只需要部署一个项目),我把vue打包成为静态文件,放置到Springboot的 static 文件中。
五、打开钉钉开发者平台,配置钉钉应用的h5公网地址
选择添加应用能力
填写公网域名
同时记得开放权限
六、打开手机钉钉,即可看到开发的页面
相关文章:

钉钉H5微应用Springboot+Vue开发分享
文章目录 说明技术路线注意操作步骤思路图 一、创建钉钉应用二、创建java项目三、创建vue项目(或uniapp项目),npm引入sdk的依赖四、拥有公网域名端口。开发环境可以使用(贝锐花生壳等工具)五、打开钉钉开发者平台&…...

项目:微服务即时通讯系统客户端(基于C++QT)]四,中间界面搭建和逻辑准备
四,中间界面搭建 前言:当项目越来越复杂的时候,或许画草图是非常好的选择 一,初始化中间窗口initMidWindow void mainWidget::initMidWindow() {//使用网格布局进行管理QGridLayout* layout new QGridLayout();//距离上方 20px 的距离&…...

【C语言】指针详解(一)
个人主页 : zxctscl 如有转载请先通知 文章目录 1.内存与地址2.指针变量与地址2.1 取地址操作符&2.2 指针变量2.3 指针类型2.4 解引用操作符2.5 指针变量的大小 3. 指针变量类型的意义3.1 指针的解引用 4. const修饰指针4.1 const修饰变量4.2 const修饰指针变量…...

unity3D雨雪等粒子特效不穿透房屋效果实现(粒子不穿透模型)
做项目有时候会做天气模拟,模拟雨雪天气等等。但是容易忽略一个问题,就是房屋内不应该下雨或者下雪,这样不就穿帮了嘛。 下面就粒子穿透物体问题做一个demo。 正常下雨下雪在室内的话,你可以看到,粒子是穿透建筑的。 那要怎么模拟真实的雨雪天气,不让粒子穿透房屋建筑呢…...
ROS2安装cartographer
2. 安装Cartographer和Cartographer ROS 使用apt安装(推荐): bash sudo apt install ros-humble-cartographer-ros或者,从源代码安装: bash sudo apt-get update sudo apt-get install -y python3-wstool python3…...
kafka测试
1】确认 ZooKeeper 服务状态 为了进一步确认 ZooKeeper 服务的状态,你可以执行以下操作: 检查 ZooKeeper 服务状态: docker ps 确保 ZooKeeper 容器正在运行。 检查 ZooKeeper 日志: docker logs zookeeper 查看最新的日志条目&…...

总结C/C++中内存区域划分
目录 1.C/C程序内存分配主要的几个区域: 2.内存分布图 1.C/C程序内存分配主要的几个区域: 1、栈区 2、堆区 3、数据段(静态区) 4.代码段 2.内存分布图 如图: static修饰静态变量成员——放在静态区 int globalVar 是…...

第168天:应急响应-ELK 日志分析系统Yara规则样本识别特征提取规则编写
目录 案例一:ELK 搭建使用-导入文件&监控日志&语法筛选 案例二:Yara 规则使用-规则检测&分析特征&自写规则 案例一:ELK 搭建使用-导入文件&监控日志&语法筛选 该软件是专业分析日志的工具,但是不支持安…...

MySQL 面试题及答案
MySQL 面试题及答案: 一、基础问题 什么是数据库索引?有哪些类型? 答:数据库索引是一种数据结构,用于提高数据库查询的效率。它就像一本书的目录,可以快速定位到特定的数据行。 类型主要有: …...

vue仿chatGpt的AI聊天功能--大模型通义千问(阿里云)
vue仿chatGpt的AI聊天功能–大模型通义千问(阿里云) 通义千问是由阿里云自主研发的大语言模型,用于理解和分析用户输入的自然语言。 1. 创建API-KEY并配置环境变量 打开通义千问网站进行登录,登陆之后创建api-key,右…...

养老院管理系统(含源码+sql+视频导入教程+文档)
👉文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 养老院管理系统拥有两种角色:管理员和护工 管理员:用户管理、老人信息管理、事故记录管理、入住费用管理、护工薪资管理、护工请假管理、床位管理、请假管理等 护…...

大数据的挑战是小文件
小文件可能会给存储平台及其支持的应用程序带来大问题。在 Google 上搜索 “small files performance” 会产生 2M 的结果。这篇博文将更深入地研究小文件问题,深入研究其根源并总结解决方案。 问题陈述 出于本讨论的目的,小文件通常被视为小于 64 KB …...

迁移学习案例-python代码
大白话 迁移学习就是用不太相同但又有一些联系的A和B数据,训练同一个网络。比如,先用A数据训练一下网络,然后再用B数据训练一下网络,那么就说最后的模型是从A迁移到B的。 迁移学习的具体形式是多种多样的,比如先用A训练…...
MCUboot 和 U-Boot区别
MCUboot 和 U-Boot 都是用于嵌入式系统的引导加载程序,但它们在一些方面存在区别: 功能特性 安全特性侧重不同 MCUboot :更专注于安全引导方面,强调安全启动、固件完整性验证和加密等安全功能。它提供了强大的安全机制来防止恶意…...

Apache OFBiz SSRF漏洞CVE-2024-45507分析
Apache OFBiz介绍 Apache OFBiz 是一个功能丰富的开源电子商务平台,包含完整的商业解决方案,适用于多种行业。它提供了一套全面的服务,包括客户关系管理(CRM)、企业资源规划(ERP)、订单管理、产…...

计算机毕业设计 饮食营养管理信息系统的设计与实现 Java实战项目 附源码+文档+视频讲解
博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精…...

828华为云征文|华为云Flexus云服务器X实例部署——盲盒抽奖商城系统以及编译发布小程序
盲盒抽奖商城系统使用 thinkphp6.0 uniapp 开发,做到了全移动端兼容。一个系统不仅可以打包 小程序 还可以 打包APP ,H5 华为云Flexus云服务器X实例在安装搭建盲盒商城小程序方面具有显著优势,这些优势主要体现在以下几个方面: …...
优化理论及应用精解【12】
文章目录 最优化基础基本概念一、目标函数二、约束条件三、约束函数 可行域与可行点可行点可行域可行点与可行域的关系示例 最优值与可行域的关系1. 最优值一定在可行域内取得2. 可行域定义了最优解的搜索空间3. 最优值的存在性与可行域的性质有关4. 最优值与可行域的边界关系示…...
excel 填充内容的公式
多行填充快捷方式: 使用“CtrlEnter”键,这样所有选中的空单元格前就会自动添加上相同的字符。 对于多行填充,Excel提供了几个快捷键来提高工作效率: “CtrlR”用于向右填充数据。如果你在表格的某一列输入了数据,选…...

这款工具在手,前端开发轻松搞定!
这款工具在手,前端开发轻松搞定! 引言 在之前的一篇文章中,已经给大家分享了一款AI助手。尽管该助手能够生成前端代码,但遗憾的是缺少了实时预览的功能。而现在,这一缺憾已经被弥补——你只需要描述你的设计想法&…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》
🧠 LangChain 中 TextSplitter 的使用详解:从基础到进阶(附代码) 一、前言 在处理大规模文本数据时,特别是在构建知识库或进行大模型训练与推理时,文本切分(Text Splitting) 是一个…...

算法—栈系列
一:删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...

数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...
【大厂机试题解法笔记】矩阵匹配
题目 从一个 N * M(N ≤ M)的矩阵中选出 N 个数,任意两个数字不能在同一行或同一列,求选出来的 N 个数中第 K 大的数字的最小值是多少。 输入描述 输入矩阵要求:1 ≤ K ≤ N ≤ M ≤ 150 输入格式 N M K N*M矩阵 输…...

NineData数据库DevOps功能全面支持百度智能云向量数据库 VectorDB,助力企业 AI 应用高效落地
NineData 的数据库 DevOps 解决方案已完成对百度智能云向量数据库 VectorDB 的全链路适配,成为国内首批提供 VectorDB 原生操作能力的服务商。此次合作聚焦 AI 开发核心场景,通过标准化 SQL 工作台与细粒度权限管控两大能力,助力企业安全高效…...