java钉钉微信qq扫码登录

概述

第三方接口其实比较简单,按照文档来操作即可,代码也就那点,最费时间的反而是在对接系统的账号的申请上,不建议个人申请很麻烦,还是让公司运维申请企业账号。

作为一名合格的开人人员,不仅仅是把第三方接口调通拿到你想要的数据就行,对接第三方接口需要注意的反而实他们的接口限制条件,这些不注意的反而会成为系统的卡点(系统用户量很少并发很少可以忽略)。

比如并发量和请求量,这些限制条件是否满足系统需求,接口防刷也需要考虑,第三方的接口请求量一般是有限制的,不注意的话一下子把你和个月的请求数给刷完了。

主要步骤就是:

官网文档:
QQ对接
微信对接
钉钉对接

1、授权

前端页面获取AppId和回调地址组装二维码,用户去微信/钉钉/qq扫码授权页面,用户授权后会自动跳转到我们的回调地址并携带授权码。

2、获取openId

前端传入授权码调用后端接口,后端根据授权码调用微信/qq/钉钉的接口获取用户信息中的openId

3、关联用户完成登录操作

根据openId查询用户授权绑定表,如果存在,直接后台生成对应用户的我们系统的token给前端,完成登录操作。

如果没有,可能有两种情况需要区分,第一种是用户不存在,第二种是用户存在,但是没有绑定过,我们不知道openId对应我们系统的哪个用户,需要页面再次引导用户输入手机号信息判断用户手否存在,如果不存在让用户去注册页面注册,实现注册并绑定,如果存在就登录并绑定。

很久之前第二步获取钉钉/微信/qq用户信息的时候,是可以拿到手机号的,但为了安全性和隐私性早就不会返回这种信息了,所以我们只能依赖openId或者unionId来绑定用户。

数据库设计:

CREATE TABLE `user_auth_bind` ( `unid` varchar(50) NOT NULL COMMENT '主键id',`user_code` varchar(50) NOT NULL COMMENT '用户id',`auth_unid` varchar(255) NOT NULL COMMENT '授权方唯一id',`auth_type` varchar(10) NOT NULL COMMENT '授权类型,wx:微信;dd:钉钉;qq: QQ ',`business_type` varchar(10) NOT NULL COMMENT '业务类型,区分多个系统使用',PRIMARY KEY (`unid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='第三方登录授权绑定表'

后端核心代码实现:

<dependency><groupId>com.aliyun</groupId><artifactId>dingtalk</artifactId><version>1.5.58</version>
</dependency>

代码:

	String unionId = "";try {if (AuthTypeEnum.WECHAT.getType().equals(authType)) {// 微信小程序if (AppKey.APPLET.equals(appKey)) {unionId = getWeChatAppletUnionId(appId, appSecret, code);}else {unionId = getWeChatUnionId(appId, appSecret, code);}}if (AuthTypeEnum.DINGTALK.getType().equals(authType)) {unionId = getDingTalkUnionId(appId, appSecret, code);}if (AuthTypeEnum.QQ.getType().equals(authType)) {// QQ的回调地址必填AuthAppResponseDto authAppInfo = getAuthAppInfo(sid, authType);unionId = getQQUnionId(appId, appSecret, authAppInfo.getRedirectUrl(), code);}} catch (Exception e) {logger.error("获取第三方授权登录用户信息异常,errMsg= {}", e.getMessage());throw new BizServiceException(AuthErrorCode.AUTH_LOGIN_APP_GET_USER_INFO_ERROR);}/*** 获取微信小程序唯一id* @param appId 应用id* @param appSecret 秘钥* @param code 授权码* @return 唯一id*/private String getWeChatAppletUnionId(String appId, String appSecret, String code) {String getAccessTokenUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code";String response = restTemplate.getForObject(getAccessTokenUrl, String.class);JSONObject accessTokenJson = JSONObject.parseObject(response);logger.debug("微信小程序获取access_token返回结果:response={}", accessTokenJson);if (!ObjectUtils.isEmpty(accessTokenJson)) {if (accessTokenJson.containsKey(AuthLoginKey.UNION_ID) && StringUtils.isNotBlank(accessTokenJson.getString(AuthLoginKey.UNION_ID))) {return accessTokenJson.getString(AuthLoginKey.UNION_ID);}else {// 获取不到unionid就直接获取openidreturn accessTokenJson.getString("openid");}}return "";}/*** 获取微信用户唯一id** @param appId     应用id* @param appSecret 应用秘钥* @param code      授权码* @return 唯一id*/private String getWeChatUnionId(String appId, String appSecret, String code) {String getAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appId + "&secret=" + appSecret + "&code=" + code + "&grant_type=authorization_code";String response = restTemplate.getForObject(getAccessTokenUrl, String.class);JSONObject accessTokenJson = JSONObject.parseObject(response);logger.debug("微信获取access_token返回结果:response={}", accessTokenJson);if (!ObjectUtils.isEmpty(accessTokenJson)) {if (accessTokenJson.containsKey(AuthLoginKey.UNION_ID) && StringUtils.isNotBlank(accessTokenJson.getString(AuthLoginKey.UNION_ID))) {return accessTokenJson.getString(AuthLoginKey.UNION_ID);}if (accessTokenJson.containsKey("access_token")) {String accessToken = accessTokenJson.getString("access_token");String openId = accessTokenJson.getString("openid");String getUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";String getUserInfoResponse = restTemplate.getForObject(getUserInfoUrl, String.class);JSONObject jsonObject = JSONObject.parseObject(getUserInfoResponse);logger.debug("微信获取用户信息返回结果:response={}", jsonObject);return jsonObject.getString(AuthLoginKey.UNION_ID);}}return "";}/*** 获取QQ用户唯一id** @param appId     应用id* @param appSecret 应用秘钥* @param redirectUri 回调地址 QQ的必填* @param code 授权码* @return 唯一id*/private String getQQUnionId(String appId, String appSecret, String redirectUri, String code) {String getAccessTokenUrl = "https://graph.qq.com/oauth2.0/token" +"?grant_type=authorization_code" +"&fmt=json" +"&need_openid=1" +"&client_id=" + appId +"&client_secret=" + appSecret +"&redirect_uri=" + URLEncodeUtil.encode(redirectUri) +"&code=" + code;String response = restTemplate.getForObject(getAccessTokenUrl, String.class);JSONObject accessTokenJson = JSONObject.parseObject(response);logger.debug("QQ获取access_token返回结果:response={}", accessTokenJson);if (!ObjectUtils.isEmpty(accessTokenJson)) {if (accessTokenJson.containsKey(AuthLoginKey.OPENN_ID) && StringUtils.isNotBlank(accessTokenJson.getString(AuthLoginKey.OPENN_ID))) {return accessTokenJson.getString(AuthLoginKey.OPENN_ID);}}return "";}/*** 获取钉钉用户唯一id** @param appId     应用id* @param appSecret 应用秘钥* @param code      授权码* @return 唯一id*/private String getDingTalkUnionId(String appId, String appSecret, String code) throws Exception {com.aliyun.dingtalkoauth2_1_0.Client client = authClient();GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest().setClientId(appId).setClientSecret(appSecret).setCode(code).setGrantType("authorization_code");GetUserTokenResponse getUserTokenResponse = client.getUserToken(getUserTokenRequest);logger.debug("钉钉获取access_token返回结果:response={}", getUserTokenResponse);if (!ObjectUtils.isEmpty(getUserTokenResponse.getBody()) && !ObjectUtils.isEmpty(getUserTokenResponse.getBody().accessToken)) {String accessToken = getUserTokenResponse.getBody().getAccessToken();String userInfo = getUserInfo(accessToken);JSONObject jsonObject = JSONObject.parseObject(userInfo);logger.debug("钉钉获取用户信息返回结果:response={}", jsonObject);return jsonObject.getString("unionId");}return "";}/*** 获取钉钉用户个人信息** @param accessToken 令牌* @return 用户信息*/private String getUserInfo(String accessToken) throws Exception {com.aliyun.dingtalkcontact_1_0.Client client = contactClient();GetUserHeaders getUserHeaders = new GetUserHeaders();getUserHeaders.xAcsDingtalkAccessToken = accessToken;// 获取用户个人信息,如需获取当前授权人的信息,unionId参数必须传mereturn JSON.toJSONString(client.getUserWithOptions("me", getUserHeaders, new RuntimeOptions()).getBody());}private static com.aliyun.dingtalkoauth2_1_0.Client authClient() throws Exception {Config config = new Config();config.protocol = "https";config.regionId = "central";return new com.aliyun.dingtalkoauth2_1_0.Client(config);}private static com.aliyun.dingtalkcontact_1_0.Client contactClient() throws Exception {Config config = new Config();config.protocol = "https";config.regionId = "central";return new com.aliyun.dingtalkcontact_1_0.Client(config);}

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

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

相关文章

力扣82-链表、迭代 的思考

题目解读 给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 两个示范 思考 返回链表&#xff1a;返回更新链表后的头结点&#xff1b; 更新链表&#xff1a;判断重复元素&#xff0c;改变指针…

centos7 宝塔php7安装mongodb扩展

一、下载、解压源码 下载地址&#xff1a;https://pecl.php.net/package/mongodb 1 2 wget -c https://pecl.php.net/get/mongodb-1.5.3.tgz tar -zxvf mongodb-1.5.3.tgz 二、编译安装源码 1 2 3 4 cd mongodb-1.5.3 /www/server/php/70/bin/phpize ./configure --with-p…

Day56|动态规划part16:583. 两个字符串的删除操作、72. 编辑距离、编辑距离总结篇

583. 两个字符串的删除操作 我的方法&#xff0c;先求出两者的最长公共子序列长度&#xff0c;再用两个字符串的长度相减就是两者分别要做操作的步数&#xff1a; class Solution {public int minDistance(String word1, String word2) {int[][] dp new int[word1.length() …

模型剪枝-Network Slimming算法分析

代码见文末 论文地址&#xff1a;Learning Efficient Convolutional Networks through Network Slimming ICCV 2017 Open Access Repository 1.概述 由于边缘设备的限制&#xff0c;在模型的部署中经常受到模型大小、运行内存、计算量的限制。之前的方法要么只能解决其中一个…

设计模式之模板模式TemplatePattern(五)

一、模板模式介绍 模板方法模式&#xff08;Template Method Pattern&#xff09;&#xff0c;又叫模板模式&#xff08;Template Pattern&#xff09;&#xff0c; 在一个抽象类公开定义了执行它的方法的模板。它的子类可以更需要重写方法实现&#xff0c;但可以成为典型类中…

Noisy:一款功能强大的DNS和HTTPS网络流量噪声生成工具

关于Noisy Noisy是一款功能强大的DNS和HTTP/S网络流量噪音生成工具&#xff0c;该工具基于Python开发&#xff0c;可以帮助广大研究人员在进行常规网络浏览时&#xff0c;在后台生成随机的HTTP/DNS网络流量噪声&#xff0c;并以此来提升网络通信数据的安全性和隐蔽性。 支持的…

第7篇:创建Nios II工程之控制LED<二>

Q&#xff1a;上一期我们完成了Quartus硬件工程部分&#xff0c;本期我们创建Nios II软件工程这部分。 A&#xff1a;创建完BSP和Nios II Application之后&#xff0c;在source文件main.c中添加LED控制代码&#xff1a;system.h头文件包含了Platform Designer系统中IP的硬件信…

【C语言】文件操作(万字解读超详细解析)

最好的时光&#xff0c;在路上;最好的生活&#xff0c;在别处。独自上路去看看这个世界&#xff0c;你终将与最好的自己相遇。&#x1f493;&#x1f493;&#x1f493; 目录 • ✨说在前面 &#x1f34b;知识点一&#xff1a;什么是文件&#xff1f; • &#x1f330;1.程序…

【分布式通信】NPKit,NCCL的Profiling工具

NPKit介绍 NPKit (Networking Profiling Kit) is a profiling framework designed for popular collective communication libraries (CCLs), including Microsoft MSCCL, NVIDIA NCCL and AMD RCCL. It enables users to insert customized profiling events into different C…

Java解决O(1) 时间插入、删除和获取随机元素

Java解决O(1) 时间插入、删除和获取随机元素 01 题目 实现RandomizedSet 类&#xff1a; RandomizedSet() 初始化 RandomizedSet 对象bool insert(int val) 当元素 val 不存在时&#xff0c;向集合中插入该项&#xff0c;并返回 true &#xff1b;否则&#xff0c;返回 false …

Android UI:动画:视图动画

文章目录 定义两种创建方式 代码:直接创建Animation对象XML:加载xml文件API 类 动画类 AnimationAlphaAnimationScaleAnimationTranlateAnimationRotateAnimationAnimationSet小结变化速率类 InterpolatorAccelerateInterpolatorDecelerateInterpolatorAccelerateDecelerateI…

acwing算法提高之数据结构--AC自动机

目录 1 介绍2 训练3 参考 1 介绍 本博客用来记录AC自动机相关题目。 AC自动机是以Trie的结构为基础&#xff0c;结合KMP的思想建立的自动机&#xff0c;用于解决多模式匹配等任务。 2 训练 题目1&#xff1a;1282搜索关键词 C代码如下&#xff0c; #include <cstdio&g…

STM32 HAL库F103系列之IIC实验

IIC总线协议 IIC总线协议介绍 IIC&#xff1a;Inter Integrated Circuit&#xff0c;集成电路总线&#xff0c;是一种同步 串行 半双工通信总线。 总线就是传输数据通道 协议就是传输数据的规则 IIC总线结构图 ① 由时钟线SCL和数据线SDA组成&#xff0c;并且都接上拉电阻…

[华为OD] C卷 5G网络 现需要在某城市进行5G网络建设,已经选取N个地点设置5G基站 200

题目 现需要在某城市进行5G网络建设&#xff0c;已经选取N个地点设置5G基站&#xff0c;编号固定为1到N,接 下来需要各个基站之间使用光纤进行连接以确保基站能互联互通&#xff0c;不同基站之间架设光纤的成 本各不相同&#xff0c;且有些节点之间已经存在光纤相连&#…

try-catch-finally的省略与springboot

在 Java 中&#xff0c;try-catch 块是用于捕获和处理异常的结构&#xff0c;它可以帮助您在代码中处理可能发生的异常情况。在某些情况下&#xff0c;您可能希望省略 try-catch 块并将异常向上抛出&#xff0c;让调用者处理异常。这种情况通常适用于以下情况&#xff1a; 方法…

python项目入门新手攻略

最近工作需要接手了代码量比较大的python开发的项目&#xff0c;平时写python不多&#xff0c;记录一下如何熟悉项目。 分析调用流程-pycallgraph 因为代码量比较大&#xff0c;所以希望通过工具生成代码调用流程&#xff0c;因此用到了pycallgraph。 pycallgraph&#xff0…

boost::asio::ip::tcp::acceptor::reuse_address

boost::asio::ip::tcp::acceptor::reuse_address 是一个选项&#xff0c;用于设置 tcp::acceptor 对象是否可以在绑定到一个地址后立即重新使用该地址。 在网络编程中&#xff0c;当服务器程序关闭后&#xff0c;操作系统可能会保留 socket 使用的地址一段时间。在这段时间内&…

【AIGC调研系列】InternVL开源多模态模型与GPT-4V的性能对比

InternVL和GPT-4V都是多模态模型&#xff0c;但它们在性能、参数量以及应用领域上有所不同。 InternVL是一个开源的多模态模型&#xff0c;其参数量为60亿&#xff0c;覆盖了图像/视频分类、检索等关键任务&#xff0c;并在32个视觉-语言基准测试中展现了卓越性能[2]。InternV…

linux操作系统,进入救援模式的方法

准备好操作系统的 ISO 文件 或 刻录好的U盘启动盘 登录服务器 BMC 管理界面&#xff0c;选择镜像之后&#xff0c;点击 “启动媒体”&#xff08;如果使用U盘启动盘 则跳过这一步骤&#xff0c;直接看下一步&#xff09; 重启服务器&#xff0c;开机界面一般按键盘 “Delete”…

数据库管理工具

Heidisql HeidiSQL是一款简洁的图形化的数据库管理工具&#xff0c;支持MySQL、SQLServer、PostgreSQL、SQLite等多种数据库。HeidiSQL提供了一个用于在数据库浏览之间切换 SQL 查询和标签带有语法突出显示的简单易用的界面。其它功能包括BLOB 和 MEMO 编辑&#xff0c;大型SQ…