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"
}
好了,本期就到這裡了,要是對你還有點作用的話,還請幫忙點贊,評論,要是能夠點個關注 或 轉發到你的朋友圈,這將是對我最大的鼓勵
技術是開放的,我們的心態更應如此,我們擁抱變化,心向陽光,堅定不移的實踐我們的每一個想法。
作者:小魔童哪吒
本作品採用《CC 協議》,轉載必須註明作者和本文連結