JustAuth实现多个钉钉扫码登录

需求:

实现多个钉钉组织的用户绑定和扫码登录。

JustAuth框架实现钉钉扫码登录用到的dingTalk接口:

  • https://oapi.dingtalk.com/connect/qrconnect
  • https://oapi.dingtalk.com/connect/oauth2/sns_authorize
  • 根据sns临时授权码获取用户信息 https://oapi.dingtalk.com/sns/getuserinfo_bycode
  • 根据unionid获取用户userid https://oapi.dingtalk.com/topapi/user/getbyunionid
  • 根据userid获取用户详情 https://oapi.dingtalk.com/topapi/v2/user/get

实现原理:

    1. 加载多个钉钉组织的账号信息并缓存(appKey,appSecret,agentId);

可参考RuoYi的ruoyi-common-social模块的实现,用一个map存储多个钉钉的配置信息;

public class SocialProperties {/*** 是否启用*/private Boolean enabled;/*** 授权类型*/private Map<String, SocialLoginConfigProperties> type;/*** 前端外网访问地址*/private String address;}

扩展:

钉钉可做到页面维护,入库保存,服务启动时可从数据库加载至缓存,有修改时更新缓存,类似下图:

登录应用系统绑定钉钉:
    1. 钉钉扫码,二维码为“钉钉认证二维码”部分的内嵌二维码方式登录授权;
    2. 授权成功后钉钉回调回调地址并追加code和state;
    3. 拿到code后调用根据sns临时授权码获取用户信息;
    4. 保存openId或unionId和userId的关系;
扫码登录应用系统:
    1. 创建钉钉认证二维码见下文(钉钉认证二维码部分,2种方式都可以);
    2. 钉钉扫码后会追加code和state至回调地址;
    3. 拿到code后调用根据sns临时授权码获取用户信息;
    4. 比对用户信息的openId或unionId,匹配则登录成功跳转至应用系统首页,不匹配则跳转至登录页并做出失败提醒;

钉钉认证二维码:

使用钉钉提供的页面登录授权:
    1. 构造认证请求:
AuthRequest authRequest = new AuthDingTalkRequest(AuthConfig.builder().clientId("Client ID").clientSecret("Client Secret").redirectUri("应用回调地址").build());
    1. 生成授权地址:
String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
    1. 访问授权地址:

这个链接我们可以直接后台重定向跳转,也可以返回到前端后,前端控制跳转。前端控制的好处就是,可以将第三方的授权页嵌入到iframe中,适配网站设计。

    1. 效果图:

内嵌二维码方式登录授权:
    1. 页面中引入钉钉扫码登录JSSDK
<script src="https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js"></script>
    1. 在需要扫码登录的地方,调用如下方法:
<!-- STEP1:在HTML中添加包裹容器元素 -->
<div id="self_defined_element" class="self-defined-classname"></div>
<style>/* STEP2:指定这个包裹容器元素的CSS样式,尤其注意宽高的设置 */.self-defined-classname {width: 300px;height: 300px;}
</style>
<script>// STEP3:在需要的时候,调用 window.DTFrameLogin 方法构造登录二维码,并处理登录成功或失败的回调。window.DTFrameLogin({id: 'self_defined_element',width: 300,height: 300,},{redirect_uri: encodeURIComponent('http://www.aaaaa.com/a/b/'),client_id: 'dingxxxxxxxxxxxx',scope: 'openid',response_type: 'code',state: 'xxxxxxxxx',prompt: 'consent',},(loginResult) => {const {redirectUrl, authCode, state} = loginResult;// 这里可以直接进行重定向window.location.href = redirectUrl;// 也可以在不跳转页面的情况下,使用code进行授权console.log(authCode);},(errorMsg) => {// 这里一般需要展示登录失败的具体原因,可以使用toast等轻提示console.error(`errorMsg of errorCbk: ${errorMsg}`);},);
</script>
    1. 参数说明((TypeScript语言描述)):
// ********************************************************************************
// window.DTFrameLogin方法定义
// ********************************************************************************
window.DTFrameLogin: (frameParams: IDTLoginFrameParams, // DOM包裹容器相关参数loginParams: IDTLoginLoginParams, // 统一登录参数successCbk: (result: IDTLoginSuccess) => void, // 登录成功后的回调函数errorCbk?: (errorMsg: string) => void,         // 登录失败后的回调函数
) => void;// ********************************************************************************
// DOM包裹容器相关参数
// ********************************************************************************
// 注意!width与height参数只用于设置二维码iframe元素的尺寸,并不会影响包裹容器尺寸。
// 包裹容器的尺寸与样式需要接入方自己使用css设置
interface IDTLoginFrameParams {id: string;      // 必传,包裹容器元素ID,不带'#'width?: number;  // 选传,二维码iframe元素宽度,最小280,默认300height?: number; // 选传,二维码iframe元素高度,最小280,默认300
}// ********************************************************************************
// 统一登录参数
// ********************************************************************************
// 参数意义与“拼接链接发起登录授权”的接入方式完全相同(缺少部分参数)
// 增加了isPre参数来设定运行环境
interface IDTLoginLoginParams {redirect_uri: string;     // 必传,注意url需要encoderesponse_type: string;    // 必传,值固定为codeclient_id: string;        // 必传scope: string;            // 必传,如果值为openid+corpid,则下面的org_type和corpId参数必传,否则无法成功登录prompt: string;           // 必传,值为consent。state?: string;           // 选传org_type?: string;        // 选传,当scope值为openid+corpid时必传corpId?: string;          // 选传,当scope值为openid+corpid时必传exclusiveLogin?: string;  // 选传,如需生成专属组织专用二维码时,可指定为true,可以限制非组织帐号的扫码exclusiveCorpId?: string; // 选传,当exclusiveLogin为true时必传,指定专属组织的corpId
}// ********************************************************************************
// 登录成功后返回的登录结果
// ********************************************************************************
interface IDTLoginSuccess {redirectUrl: string;   // 登录成功后的重定向地址,接入方可以直接使用该地址进行重定向authCode: string;      // 登录成功后获取到的authCode,接入方可直接进行认证,无需跳转页面state?: string;        // 登录成功后获取到的state
}
    1. 效果图:

前端生成授权页面,可以自己放置到任何需要的地方,相比jssdk生成的比钉钉提供的授权要漂亮很多,再也没有蓝底的加持。

附内嵌二维码的源码(Vue3):

<template><div id="login_container"></div>
</template><script setup name="dingTalkAuthQrcode">
import { ref, onMounted } from "vue";
import { generateState } from "@/api/system/social/auth";const props = defineProps({appKey: {type: String},redirectUrl: {type: String},appCode: {type: String}
});
const emits = defineEmits(["callback"]);
// 假设你已经有了钉钉的 appKey 和 appSecret
const appKey = props.appKey || "your dingtalk appKey";
const redirectUrl = props.redirectUrl || '从配置文件中读取';
const stateCode = ref(null);
const handleMessage = async (event) => {var origin = event.origin;if (origin === "https://login.dingtalk.com") { //判断是否来自ddLogin扫码事件。const loginTmpCode = event.data;//获取到loginTmpCode后就可以在这里构造跳转链接进行跳转了,generateState()生成了stateconst res = await generateState();if (res && res.code === 200) {stateCode.value = res.data;}// 使用代理避免跨域请求,代理了/connectconst dingTalkForwardUrl = `/connect/connect/oauth2/sns_authorize?appid=${appKey}&response_type=code&scope=snsapi_login&state=` + stateCode.value + `&redirect_uri=${redirectUrl}` + "&loginTmpCode=" + loginTmpCode;// 模拟请求网页跳转const response = await fetch(dingTalkForwardUrl);// 解析网页302成功后的code 和 stateconst urlParams = new URLSearchParams(new URL(response.url).search);const code = urlParams.get("code");const state = urlParams.get("state");if (code && state) {emits("callback", { "code": code, "state": state });}}
};
// 在组件挂载后调用生成二维码的函数
onMounted(() => {var obj = DDLogin({id: "login_container",goto: encodeURIComponent(`https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${appKey}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=${redirectUrl}`),style: "border:none;background-color:#FFFFFF;",width: "365",height: "400"});if (typeof window.addEventListener != "undefined") {window.addEventListener("message", handleMessage);}
});
onUnmounted(() => {window.removeEventListener("message", handleMessage)
});
</script>

总结:

  1. JustAuth框架实现钉钉授权登录是比较容易实现的,按照官方教程操作即可。本文介绍的是多个钉钉组织,实现思想等价于钉钉+企业微信+gitee这样;
  2. 使用临时码免登的方式再dingTalk开放平台已经被标记为过时,新的方法是使用tmpCode换取用户信息和现在的JustAuth是不兼容的,后续可期待下JustAuth是否做兼容。

参考文档:

  1. 钉钉扫码登录详细流程可参考JustAuth的官方文档:钉钉登录 | JustAuth
  2. 钉钉开放平台文档:扫码登录第三方网站 - 钉钉开放平台
  3. 钉钉开发平台新的身份验证(免登)文档:概述 - 钉钉开放平台

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/43733.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java基础之Stringjoiner

Stringjioiner的概述 StringJoiner跟StringBuilder一样&#xff0c;也可以看成是一个容器&#xff0c;创建之后里面的内容是可变的。作用:提高字符串的操作效率&#xff0c;而且代码编写特别简洁&#xff0c;但是目前市场上很少有人用。 Stringjoiner的构造方法 Stringjoiner…

软件许可证优化怎么做最好!

在当今数字化发展的浪潮中&#xff0c;软件许可证的优化成为了 IT 总监们面临的一项重要挑战。在许可数量受限的情况下&#xff0c;如何将现有许可发挥最大利用率&#xff0c;是一个亟待解决的问题。 信息采集是优化的基础。 我们需要采集关于软件使用频率、使用时长、用户部门…

05.C1W4.Machine Translation and Document Search

往期文章请点这里 目录 OverviewWhat you’ll be able to do!Learning Objectives Transforming word vectorsOverview of TranslationTransforming vectors Align word vectorsSolving for RFrobenius normFrobenius norm squaredGradient K nearest neighborsFinding the tr…

Sentinel-1 Level 1数据处理的详细算法定义(二)

《Sentinel-1 Level 1数据处理的详细算法定义》文档定义和描述了Sentinel-1实现的Level 1处理算法和方程&#xff0c;以便生成Level 1产品。这些算法适用于Sentinel-1的Stripmap、Interferometric Wide-swath (IW)、Extra-wide-swath (EW)和Wave模式。 今天介绍的内容如下&…

解决打印PDF文本不清楚的处理办法

之前打印PDF格式的电子书&#xff0c;不清晰&#xff0c;影响看书的心情&#xff0c;有时看到打印的书的质量&#xff0c;根本不想看&#xff0c;今天在打印一本页数不多&#xff0c;但PDF格式的书感觉也不太清楚&#xff0c;我想应该有办法解决&#xff0c;我使用的是解决福昕…

2017年,我成为了技术博主

2017年9月&#xff0c;我已经大三了。 >>上一篇&#xff08;爪哇&#xff0c;我初窥门径&#xff09; 我大二学了很多java技术&#xff0c;看似我一会就把javaweb/ssh/ssm这些技术栈给学了。 这些技术确实不难&#xff0c;即便是我&#xff0c;我都能学会&#xff0c;…

可以添加todo清单桌面小组件的便签哪个好?

在我们快节奏的生活中&#xff0c;有效的时间管理和任务追踪是必不可少的。为了实现这一目标&#xff0c;许多人选择使用桌面便签&#xff0c;尤其是那些具有Todo清单桌面小组件的便签。但是&#xff0c;面对市场上众多选择&#xff0c;可以添加todo清单桌面小组件的便签哪个好…

14 Portainer轻量级图形化监控

目录 Portainer&#xff1a;Docker轻量级可视化工具 1. 安装与访问 2. 使用 3. Portainer配置 nginx Portainer&#xff1a;Docker轻量级可视化工具 Portainer是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用于方便地管理Docker环境&#xff0c;包括单机…

db期末复习自用[应试向 附习题]

第一章 数据库系统实现整体数据的结构化&#xff0c;主要特征之一&#xff0c;是db区别于文件系统的本质区别。 数据库系统三个阶段&#xff1a;人工、文件、数据库系统。 数据库管理系统的功能&#xff1a;数据库定义、操纵 、&#xff08;保护、存储、维护&#xff09;、数…

Jmeter在信息头中设置Bearer与 token 的拼接值

思路&#xff1a;先获取token&#xff0c;将token设置成全局变量&#xff0c;再与Bearer拼接。 第一步&#xff1a;使用提取器将token值提取出来&#xff0c;使用setProperty函数将提取的token值设置成全局变量&#xff0c;在登录请求后面添加BeanShell取样器 或者 BeanShell后…

数据结构作业/2024/7/9

2>实现双向循环链表的创建、判空、尾插、遍历、尾删、销毁 fun.c #include "head.h" //1.双向循环链表的创建 doubleloop_ptr create_list() …

STM32实战篇:按键控制LED

按键控制LED 功能要求 有两个按键&#xff0c;分别控制两个LED灯。当按键按下后&#xff0c;灯的亮暗状态改变。实物如下图所示&#xff1a; 由图可知&#xff0c;按键一端直接接地&#xff0c;故另一端所对应IO引脚的输入模式应该为上拉输入模式。 实现代码 #include "…

PHP验证日本免费电话号码格式

首先&#xff0c;您需要了解免费电话号码的格式。 日本免费电话也就那么几个号段&#xff1a;0120、0990、0180、0570、0800等开头的&#xff0c;0800稍微特殊点&#xff0c;在手机号里面有080 开头&#xff0c;但是后面不一样了。 关于免费电话号码的划分&#xff0c;全部写…

【前端从入门到精通:第十二课: JS运算符及分支结构】

JavaScript运算符 算数运算符 关于自增自减运算 自增或者自减运算就是在本身的基础上进行1或者-1的操作 自增或者自减运算符可以在变量前也可以在变量后&#xff0c;但是意义不同 自增自减运算符如果在变量前&#xff0c;是先进行自增或者自减运算&#xff0c;在将变量给别人用…

idea集成本地tomcat

由于网课老师使用的是eclipse,但是……本人用的是idea,所以不得不去找教程。 解决方案1&#xff1a; https://blog.csdn.net/weixin_54048131/article/details/131359793 这个地方&#xff0c;路径一定要到这个tomcat 否则不识别&#xff1a; 这里的JRE也要配置一下 新问题&…

ESP32的I2S引脚及支持的音频标准使用说明

ESP32 I2S 接口 ESP32 有 2 个标准 I2S 接口。这 2 个接口可以以主机或从机模式&#xff0c;在全双工或半双工模式下工作&#xff0c;并且可被配置为 8/16/32/48/64-bit 的输入输出通道&#xff0c;支持频率从 10 kHz 到 40 MHz 的 BCK 时钟。当 1 个或 2 个 被配置为主机模式…

数据结构 实验 3

题目一&#xff1a;最短路径dijkstra算法 一、实验目的 熟练图的邻接矩阵和邻接表表示法掌握图的最短路径Dijkstra算法的基本思想用C语言实现Dijkstra算法 二、实验内容 从键盘输入的数据创建图&#xff08;图的存储结构采用邻接矩阵&#xff09;&#xff0c;设计Dijkstra算…

鸿蒙语言基础类库:【@ohos.util.Deque (线性容器Deque)】

线性容器Deque 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 Deque&#xff08;double ended queue&#xff09;根据循环队列的数据结构实现&#xff0c;符合先进先出以及先进后出的特点&…

Redis 实现高并发库存扣减方案

背景 公司的电商系统下单 操作库存是一个频繁操作&#xff0c;需要高效地扣减库存&#xff0c;把对销售库存的操作抽出来独立设计一个库存中心系统。 功能包括库存的批量添加、获取、下单、支付、回退等的操作。 解决的业务痛点 需要高效不超卖 方案 一、使用msql乐观锁 …

PostgreSQL 如何解决数据迁移过程中的数据类型不匹配问题?

文章目录 一、了解常见的数据类型不匹配情况1. 整数类型差异2. 浮点数类型差异3. 字符类型差异4. 日期和时间类型差异 二、解决数据类型不匹配的一般策略1. 数据转换2. 调整数据库表结构3. 数据清洗和预处理 三、PostgreSQL 中的数据类型转换函数1. 数值类型转换2. 字符类型转换…