OAUTH之 钉钉第三方授权登录

2023-10-27

OAUTH之钉钉第三方授权登录

hello,我是小魔童哪吒,欢迎点击关注,有更新,将第一时间呈现到你的面前

胖sir:小魔童,我今天收到了一个需求,期望我们做一个第三方登录的功能,用户可以通过第三方授权来登录我们的web

小魔童:啊哈?你有眉目吗

胖sir:那当然,我知道可以通过微信登录,钉钉登录,github登录等等呢

小魔童:那你知道都是咋实现的吗?说给我听听,让我也学一下

胖sir:你带我跑飞车吗?

小魔童:这。。 你。。 我教你如何一骑绝尘把,前提是你给我讲明白咋弄

胖sir:稳了,成交。

其实这种第三方登录的原理属于OAuth机制,主要用来颁发令牌(token),现在OAuth已经到 OAuth2.0了

用百度百科的话说:

OAuth2.0是OAuth协议的延续版本,但不向前兼容OAuth 1.0(即完全废止了OAuth1.0)。 OAuth 2.0关注客户端开发者的简易性。要么通过组织在资源拥有者和HTTP服务商之间的被批准的交互动作代表用户,要么允许第三方应用代表用户获得访问的权限。同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。2012年10月,OAuth 2.0协议正式发布为RFC 6749 [1] 。

主要有如下四种方式,简单给你列举一下:

  • 授权码
  • 隐藏式
  • 密码式
  • 客户端凭证

画一个简图来你感受一下

当然我不是给你说OAuth自身的原理和涉及的技术,我是要来直接给你说咱们咋实现我刚才说的对接钉钉的接口,因为钉钉的开发文档中间有修改过好几次,

另外文档中的表述也存在晦涩难懂的地方,鉴于你带我飞车 一骑绝尘,我就给你说说 如何获取到钉钉的授权,以及拿到使用钉钉对应公司(必须有公司管理员的权限)下的组织结构

钉钉开发文档没有golang版本的SDK和源码,那么我们就自己来实现

前期用到的工具

  • postman 做接口调试
  • golang 语言 通过 goland 编译器 做 通过 access_token 和 临时 code 获取 unionid的功能

获取access_token

请求地址

https://oapi.dingtalk.com/gettoken?appkey=xxx&appsecret=xxx

请求方法

  • GET
  • query
    • appkey
    • appsecret

此处的 appkeyappsecret 是H5微应用里面的应用数据

响应



{
    "errcode": 0,
    "access_token": "4dbda4ddb82dxxxxxx138afab15655",
    "errmsg": "ok",
    "expires_in": 7200
}

扫码 / 使用账号密码 – 获取 临时 code

参数重要说明

  • appId

    登录应用的 appId

  • redirect_uri - 回调域名

    重定向的url地址,登录成功后,网页会重定向到redirect_uri

    redirect_uri 必须要在钉钉开放平台配置好,否则会没有权限访问如下地址 ,例如该参数填百度的地址

  • LOGO地址

    自己在网络上的一张可以访问的图片地址即可,如下:

直接访问 扫码登录

https://oapi.dingtalk.com/connect/qrconnect?appid=xxxx&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=https://www.baidu.com/

使用账号密码登录第三方网站

https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=xxx&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=https://www.baidu.com/

如上2种方式登录成功后,是如下效果,暂时我们使用跳转的域名为https://www.baidu.com/ 主要是为了获取 临时code

根据 sns 临时授权码获取用户信息

通过 access_token 和 临时code 获取unionid

前置条件

  • 需要在钉钉开放平台上设置自己的服务器的出口ip白名单

请求地址

https://oapi.dingtalk.com/sns/getuserinfo_bycode?signature=xxx&timestamp=xxx&accessKey=xxx
  • POST

  • query

    • accessKey

      钉钉开放平台中,登录应用的 appId

    • timestamp

      单位: 毫秒

    • signature

      签名算法为HmacSHA256,签名数据是当前时间戳timestamp,密钥是appId对应的appSecret,使用密钥对timestamp计算签名值。

      发送HTTP请求时需要把signature进行urlEncode,如果您使用的是HTTP封装方法,请确保不要重复urlEncode

  • body

{
        "tmp_auth_code":"4a2c5695b78738d495f47bxxxxxx"
}

响应

{
        "errcode":0,
        "user_info":{
                "nick":"名字",
                "unionid":"dingdkjjojoixxxx",
                "openid":"dingsdsqwlklklxxxx",
                "main_org_auth_high_level":true
        },
        "errmsg":"ok"
}

golang 具体操作和逻辑

对于这个接口的签名计算方式,需要给你看看是如何实现的,具体实现很简单,但是对于时间戳的取值,需要注意是毫秒级别的

package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)

func main() {
	// 登录应用的 appSecret
	secret := "xxxx"

	key := []byte(secret)
	h := hmac.New(sha256.New, key)

	timestamp := time.Now().UnixNano()/1e6 + 120000 //因为我的环境时间比钉钉服务器慢2分钟,所以我这里加了2分钟

	strTimeStamp := fmt.Sprintf("%d", timestamp)

	h.Write([]byte(strTimeStamp))

	sha := h.Sum(nil)
	sig := base64.StdEncoding.EncodeToString(sha)

	mysig := url.QueryEscape(sig)

	url := fmt.Sprintf("https://oapi.dingtalk.com/sns/getuserinfo_bycode?signature=%s&timestamp=%d&accessKey=%s",
		mysig, timestamp, "xxxx")
	fmt.Println(url)

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	spaceClient := http.Client{Timeout: time.Second * time.Duration(5), Transport: tr}

	m := map[string]string{"tmp_auth_code": "67d86cb135ee3bd18d756c2d2fa1a350"}
	res, err := json.Marshal(m)
	if err != nil {
		fmt.Println(res)
		return
	}
	fmt.Println(string(res))

	req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(res))
	req.Header.Set("Content-Type", "application/json")

	rawResp, err := spaceClient.Do(req)
	if err != nil {
		fmt.Println(rawResp)
		return
	}
	if rawResp.Status != "200 OK" {
		fmt.Println("rawResp.Status != 200 ok", rawResp)
		return
	}

	body, readErr := ioutil.ReadAll(rawResp.Body)
	if readErr != nil {
		fmt.Println("ReadAll error ", readErr)
		return
	}

	fmt.Println("result --", string(body))
}

根据 unionid 获取用户 userid

请求地址

https://oapi.dingtalk.com/topapi/user/getbyunionid?access_token=xxxxxx

请求方法

  • POST

  • query

    • access_token
  • body 请求

{
 "unionid":"xxxxxx"
}

响应

{
    "errcode": 0,
    "errmsg": "ok",
    "result": {
        "contact_type": 0,
        "userid": "managerxxxx"
    },
    "request_id": "xxxxxx"
}

根据userid 获取 用户详情

请求地址

https://oapi.dingtalk.com/topapi/v2/user/get?access_token=xxxxx

请求方法

  • POST
  • query
    • access_token
  • body 请求
{
   "language":"zh_CN",
   "userid":"managerxxxxx"
}

响应

{
    "errcode": 0,
    "errmsg": "ok",
    "result": {
        "active": true,
        "admin": true,
        "avatar": "",
        "boss": false,
        "dept_id_list": [
            1
        ],
        "dept_order_list": [
            {
                "dept_id": 1,
                "order": 176299320823645512
            }
        ],
        "email": "",
        "exclusive_account": false,
        "hide_mobile": false,
        "leader_in_dept": [
            {
                "dept_id": 1,
                "leader": false
            }
        ],
        "mobile": "xxxxx",
        "name": "xxx",
        "real_authed": true,
        "role_list": [
            {
                "group_name": "默认",
                "id": 1993003008,
                "name": "主管理员"
            }
        ],
        "senior": false,
        "state_code": "86",
        "unionid": "xxxxx",
        "userid": "managerxxxxx"
    },
    "request_id": "xxxxx"
}

获取部门列表

请求地址

https://oapi.dingtalk.com/topapi/v2/department/listsub?access_token=xxxxx

请求方法

  • POST
  • query
    • access_token
  • body请求
{
        "language":"zh_CN",
        "dept_id":1
}

响应

{
    "errcode": 0,
    "errmsg": "ok",
    "result": [
        {
            "auto_add_user": true,
            "create_dept_group": true,
            "dept_id": 477856721,
            "name": "运营部",
            "parent_id": 1
        },
        {
            "auto_add_user": true,
            "create_dept_group": true,
            "dept_id": 477856722,
            "name": "设计部",
            "parent_id": 1
        }
    ],
    "request_id": "evcmse04h8op"
}

获取部门用户 userid 列表

请求地址

https://oapi.dingtalk.com/topapi/user/listid?access_token=xxxxxx

请求方法

  • POST
  • query
    • access_token
  • body 请求
{
 "dept_id":xx
}

响应

{
    "errcode": 0,
    "errmsg": "ok",
    "result": {
        "userid_list": [
            "managerxxxxx"
        ]
    },
    "request_id": "xxxxx"
}

好了,本期就到这里了,要是对你还有点作用的话,还请帮忙点赞,评论,要是能够点个关注 或 转发到你的朋友圈,这将是对我最大的鼓励

技术是开放的,我们的心态更应如此,我们拥抱变化,心向阳光,坚定不移的实践我们的每一个想法。

作者:小魔童哪吒

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OAUTH之 钉钉第三方授权登录 的相关文章

随机推荐

  • nohup训练pytorch模型时的报错以及tmux的简单使用

    问题 在使用nohup命令后台训练pytorch模型时 关闭ssh窗口 有时会遇到下面报错 WARNING torch distributed elastic agent server api Received 1 death signal
  • Mac下IDEA常用操作整理

    目录 1 完美卸载 2 常用插件 3 一些简便的常规设置 1 更改字体大小通过ctrl 鼠标滚轮 2 代码提示不区分大小写 3 修改字体 4 修改控制台输出字体 5 设置文件编码 6 自动导包 优化导包 7 设置自动编译 8 显示行号和方法
  • 给你一个链表数组,每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中,返回合并后的链表

    基本思路 想要合并多个升序的链表 前提是需要会合并两个升序排列的链表 具体的方法在之前的博文中有提及合并两个有序的链表 大概的思路和合并两个有序数组基本相同 就是在链表的最开始确定两个指针 根据指针来确定哪个链表的元素更小一点 将小的哪个链
  • Java的基础:双层循环

    所谓双层 双层 循环 就是在循环内部嵌套循环 特点 1 外层循环控制行 换行 内层循环控制列 内容输出 2 外层循环更新一次 内层循环更新一轮 语法 for 变量 i 循环条件 跟新变量 外层循环 循环操作1 for 变量 j 循环条件 跟
  • 学成在线笔记+踩坑(8)——课程预览、提交审核,Freemarker模板引擎

    导航 黑马Java笔记 踩坑汇总 JavaSE JavaWeb SSM SpringBoot 瑞吉外卖 SpringCloud 黑马旅游 谷粒商城 学成在线 牛客面试题 java黑马笔记 目录 1 模块需求分析 1 1 模块介绍 1 2 业
  • linux下使用crontab定时器,并且设置定时不执行的情况,附:项目启动遇到的一些问题和命令

    打开终端 以root用户身份登录 运行以下命令打开cron任务编辑器 crontab e 如果首次编辑cron任务 会提示选择编辑器 选择你熟悉的编辑器 比如nano或vi 并打开相应的配置文件 在编辑器中 添加一行类似以下的命令来设置定时
  • 软测入门(一)测试理念及基础知识

    软测入门理念 软件的分类 按层次划分 系统软件 应用软件 按组织划分 商业软件 开源软件 按结构划分 单机软件 软件缺陷 由来 Grace Hopper发明Cobol计算机语言 也是找出电脑程序中第一个bug的女程序员 Bug Defect
  • 上传源码到GitHub代码托管平台方法和步骤

    上传源码到GitHub的方法步骤 要在本地上传代码到GitHub第一步就是先在本地有git版本控制软件 或者你的IDE集成了git 否则一切都是徒劳 1 先下载git 地址 https git scm com downloads 2 安装G
  • 【漏洞复现】CVE-2021-45232 Apache-apisix-dashboard

    靶场搭建 修改版本号 启动环境 查看端口号为9090 用主机登录ip 9000 默认用户名 admin 默认密码 admin 点击 路由 进入路由页面 打开POC所在文件夹 cmd执行 刷新路由页面 查看路由配置 成功注入恶意 script
  • Oracle各种进程功能一览表

    在安装Oracle数据库的时候 我检查进程 发现了以下进程 功能如下 Ora pmon 是进程监视器 Process Monitor 的缩写 当取消当前的事务 或者释放进程占用的锁以及其它资源的时候 这个进程清空那些失败的进程 Ora vk
  • 谷歌Chrome小恐龙代码(自动跳,高跳,无敌,加速)

    目录 自动跳代码 无敌代码 高跳代码 可以改括号内参数 疾跑代码 可以改括号内参数 大多数浏览器都有自己的彩蛋 而今天我们分享的是谷歌Chrome 谷歌小恐龙游戏是一个浏览器自带的小游戏 断网联网状态都是可以玩的 那么如何在联网的状态下进行
  • Flask 增删改查

    基本操作 目录 基本操作 添加 查看数据 分页 详情 删除 修改 路由配置 创建模型类models from app extensions import db class Books db Model tablename tb books
  • Feign远程调用丢失请求头问题

    在业务中 需要使用A B两个模块 这些模块使用了SpringSession共享Session数据 在B模块中的业务需要用户登录后才能操作 当A调用B的业务时 在B模块中获取不到用户的Session信息 导致B模块判定该请求用户没有登录导致A
  • java基础错题总结

    1 解析 首先乘法的优先级高于加法 所以先进行y z 然后在这里 是连接符 因为头一个是字符串所以系统就认为是连接符 就成 10202 0 输出这个字符串 但如果第一个不是字符串类型的 就像 10 20 a 这个会输出 30 a 2 解析
  • gis中的加权求和工具在哪里_因果推理初探(5)——干预工具(上)

    本节将延续上一节学习的干预的有关概念 开始深入介绍几种干预的工具 后门调整 前门调整 逆概率加权等 本节将有大量公式来袭 请准备草稿纸或提前绕道 在上一节最后 我们推导出有关干预的重要公式 调整公式 它的形式如下 这个公式让我们可以通过观测
  • 为什么使用代理后不能上网了?

    在使用完代理服务器之后 有的用户可能会遇到这样的问题 明明网络正常 为什么我的浏览器不能打开网页了 今天就给大家说下具体解决方法 这里我们以IE浏览器为例 1 先打开浏览器 点击右上角的 工具 图标 然后点击下拉中的 Internet选项
  • elasticsearch中文分词器插件elasticsearch-analysis-ik远程自定义词典热更新

    IK简介 IK分词器基于词库进行分词 analysis ik内置了一些词典 主词典main dic 姓氏词典surname dic 量词词典quantifier dic 后缀词典suffix dic 介词词典preposition dic
  • vue项目创建

    默认3 默认2 自定义配置 js语法编辑器 ts 渐进式web应用程序 路由 状态管理器 css处理器 代码检查 单元测试 端对端测试 选择版本 路由是否选择历史模式 选择css预处理器 配置放在哪里 保存这个项目作为一个模版使用 npm
  • 【网络】Linux网络问题汇总(一)

    网卡设置了静态获取 仍然获取动态IP的解决方法 问题展示 网卡配置静态方式获取 仍然通过dhcp获取到了ip 且每次分配的ip都一样 root senlian cat etc sysconfig network scripts ifcfg
  • OAUTH之 钉钉第三方授权登录

    文章目录 OAUTH之钉钉第三方授权登录 前期用到的工具 获取access token 请求地址 请求方法 响应 扫码 使用账号密码 获取 临时 code 参数重要说明 直接访问 扫码登录 使用账号密码登录第三方网站 根据 sns 临时授权