首先获取appid 和 appsecret
钉钉文档有讲解
我使用的是将钉钉二维码嵌入到自己的页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
<script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
</head>
<body>
<p>123{{.title}}</p>
<div id="login_container"></div>
<p>456</p>
</body>
<script>
/*
* 解释一下goto参数,参考以下例子:
* var url = encodeURIComponent('http://localhost.me/index.php?test=1&aa=2');
* var goto = encodeURIComponent('https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=appid&response_type=code&scope=snsapi_login&state=STATE&redirect_uri='+url)
*/
var baseurl = "http://127.0.0.1:8080";
var urlapi = "/api/work/LoginByQRcode"
var goto = encodeURIComponent(
'https://oapi.dingtalk.com/connect/oauth2/sns_authorize?' +
'appid=' + "appid" +
'&response_type=code&' +
'scope=snsapi_login&' +
'state=STATE&redirect_uri=' + baseurl + urlapi);
var obj = DDLogin({
id:"login_container",//这里需要你在自己的页面定义一个HTML标签并设置id,例如<div id="login_container"></div>或<span id="login_container"></span>
goto: goto, //请参考注释里的方式
style: "border:none;background-color:#FFFFFF;",
width : "365",
height: "400"
});
//扫完码就会执行下面这些代码
var handleMessage = function (event) {
var origin = event.origin;
console.log("origin", event.origin);
if( origin == "https://login.dingtalk.com" ) { //判断是否来自ddLogin扫码事件。
var loginTmpCode = event.data; //拿到loginTmpCode后就可以在这里构造跳转链接进行跳转了
console.log("loginTmpCode", loginTmpCode);
var url1 =
"https://oapi.dingtalk.com/connect/oauth2/sns_authorize?" +
"appid=" + "appid" +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=STATE" +
"&redirect_uri=" + baseurl + urlapi +
"&loginTmpCode=" + loginTmpCode;
console.log("loginTmpCode", url1)
window.location.href=url1;
}
};
//这时候页面会跳转到钉钉自己的服务器,
//然后钉钉根据 redirecturl在跳转到你的页面
//并在url中包含了loginTmpcode,
//这时候你获得了这个code把他传给后台来获取钉钉用户信息。。
if (typeof window.addEventListener != 'undefined') {
console.log('message', handleMessage, false);
window.addEventListener('message', handleMessage, false);
} else if (typeof window.attachEvent != 'undefined') {
console.log('onmessage', handleMessage);
window.attachEvent('onmessage', handleMessage);
}
</script>
</html>
服务端通过临时授权码获取授权用户的个人信息
通过临时授权码Code获取用户信息,临时授权码只能使用一次。
这里面涉及到的几个钉钉后台接口
1
请求方式:POST(HTTPS)
请求地址:https://oapi.dingtalk.com/sns/getuserinfo_bycode?accessKey=xxx×tamp=xxx&signature=xxx
URL签名参数说明:
参数 | 说明 |
---|---|
accessKey: | 应用的appId,参见本篇文档获取appId及appSerect章节 |
timestamp: | 当前时间戳,单位是毫秒 |
signature: | 通过appSecret计算出来的签名值,签名计算方法这个方法我也用go实现了 |
请求包结构体:
{
"tmp_auth_code": "23152698ea18304da4d0ce1xxxxx"
}
返回结果:
```javascript
{
"errcode": 0,
"errmsg": "ok",
"user_info": {
"nick": "张三",
"openid": "liSii8KCxxxxx",
"unionid": "7Huu46kk" // 这个是用来查user info的
}
}
func LoginByQRcode(code string) (userid string, err error) {
var resp *http.Response
//fmt.Println("AppKey,AppSecret", AppKey, AppSecret)
//服务端通过临时授权码获取授权用户的个人信息
appKey := ""
appSecret := ""
timestamp := strconv.FormatInt(time.Now().UnixNano()/1000000, 10) // 毫秒时间戳
signature := EncodeSHA256(timestamp, appSecret) // 加密签名 加密算法见我另一个函数
url2 := fmt.Sprintf(
"https://oapi.dingtalk.com/sns/getuserinfo_bycode?accessKey=%s×tamp=%s&signature=%s",
appKey, timestamp, signature)
//fmt.Println(3, url2, )
p := struct {
Tmp_auth_code string `json:"tmp_auth_code"`
}{code} // post数据
p1, _ := json.Marshal(p)
p2 := string(p1)
p3 := strings.NewReader(p2) //构建post数据
resp, err = http.Post(url2, "application/json;charset=UTF-8", p3)
//fmt.Println(1, resp, err)
body, err := ioutil.ReadAll(resp.Body)
//fmt.Println(2, string(body), err)
var i map[string]interface{}
_ = json.Unmarshal(body, &i) ///返回的数据给i
errcode := i["errcode"].(float64)
if errcode != 0 {
return "", errors.New(fmt.Sprintf("登录错误: %s, %s", errcode, i["errmsg"].(string)))
}
unionid := i["user_info"].(map[string]interface{})["unionid"].(string) // unionid 可以用来查询userinfo
accesstoken, err := GetAccesstoken() // 获取accesstoken
if err != nil {
return "", errors.New(fmt.Sprintf("登录错误accesstoken获取失败: %s", err))
}
userid, err = GetUseridByUnionid(accesstoken,unionid)
if err != nil {
return "", errors.New(fmt.Sprintf("登录错误userid获取失败: %s", err))
}
return userid, nil
}
func GetUseridByUnionid (accesstoken, unionid string) (userid string, err error) {
//根据unionid获取userid
var resp *http.Response
url := fmt.Sprintf("https://oapi.dingtalk.com/user/getUseridByUnionid?access_token=%s&unionid=%s",
accesstoken, unionid)
resp, err = http.Get(url)
body, err := ioutil.ReadAll(resp.Body)
//fmt.Println(1, string(body), err)
var i map[string]interface{}
_ = json.Unmarshal(body, &i)
errcode := i["errcode"].(float64)
if errcode != 0 {
return "", errors.New(fmt.Sprintf("userid获取错误: %s, %s", errcode, i["errmsg"].(string)))
}
return i["userid"].(string), nil
}
几乎钉钉每一个接口都需要accesstoken参数
func GetAccesstoken() (accesstoken string, err error) {
var resp *http.Response
//var AppKey, AppSecret string
//获取access_token
url := fmt.Sprintf("https://oapi.dingtalk.com/gettoken?appkey=%s&appsecret=%s", AppKey, AppSecret)
resp, err = http.Get(url)
//fmt.Println(resp)
//fmt.Println(err)
body, err := ioutil.ReadAll(resp.Body)
var i map[string]interface{}
_ = json.Unmarshal(body, &i)
//fmt.Println(1, string(body), i["errmsg"])
if i["errcode"].(float64) == 0 {
return i["access_token"].(string), nil
}
return "", errors.New("accesstoken获取错误:"+i["errmsg"].(string))
}
下面是签名加密算法 上面用到了
func EncodeSHA256(message, secret string) string {
// 钉钉签名算法实现
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(message))
sum := h.Sum(nil) // 二进制流
message1 := base64.StdEncoding.EncodeToString(sum)
uv := url.Values{}
uv.Add("0", message1)
message2 := uv.Encode()[2:]
return message2
}