[TOC]
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
此处的 appkey
和 appsecret
是 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×tamp=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×tamp=%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"
}
好了,本期就到这里了,要是对你还有点作用的话,还请帮忙 点赞,评论 ,要是可能点个 关注 或 转发 到你的朋友圈,这将是对我最大的激励
技术是凋谢的,咱们的心态更应如此,咱们拥抱变动,心向阳光,坚韧不拔的实际咱们的每一个想法。
作者:小魔童哪吒