dingtalk.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. package dingtalk
  2. import (
  3. "crypto/hmac"
  4. "crypto/sha256"
  5. "encoding/base64"
  6. "encoding/json"
  7. "errors"
  8. "fmt"
  9. "io/ioutil"
  10. "net/http"
  11. "net/url"
  12. "strconv"
  13. "strings"
  14. "time"
  15. )
  16. // DingTalkAgent 用于钉钉交互
  17. type DingTalkAgent struct {
  18. AppSecret string
  19. AppKey string
  20. AccessToken string
  21. }
  22. // NewDingTalkAgent 钉钉交互构造函数
  23. func NewDingTalkAgent(appSecret, appKey string) *DingTalkAgent {
  24. return &DingTalkAgent{
  25. AppSecret: appSecret,
  26. AppKey: appKey,
  27. }
  28. }
  29. // GetUserIDByCode 通过临时code获取当前用户ID
  30. func (d *DingTalkAgent) GetUserIDByCode(code string) (string, error) {
  31. urlEndpoint, err := url.Parse("https://oapi.dingtalk.com/user/getuserinfo")
  32. if err != nil {
  33. return "", err
  34. }
  35. query := url.Values{}
  36. query.Set("access_token", d.AccessToken)
  37. query.Set("code", code)
  38. urlEndpoint.RawQuery = query.Encode()
  39. urlPath := urlEndpoint.String()
  40. resp, err := http.Get(urlPath)
  41. if err != nil {
  42. return "", err
  43. }
  44. body, err := ioutil.ReadAll(resp.Body)
  45. if err != nil {
  46. return "", err
  47. }
  48. // 解析钉钉返回数据
  49. var rdata map[string]interface{}
  50. err = json.Unmarshal(body, &rdata)
  51. if err != nil {
  52. return "", err
  53. }
  54. errcode := rdata["errcode"].(float64)
  55. if errcode != 0 {
  56. return "", errors.New(fmt.Sprintf("登录错误: %.0f, %s", errcode, rdata["errmsg"].(string)))
  57. }
  58. userid := rdata["userid"].(string)
  59. return userid, nil
  60. }
  61. // GetUserNameAndAvatarByUserID 通过userid获取当前用户姓名和头像
  62. func (d *DingTalkAgent) GetUserNameAndAvatarByUserID(userid string) (string, string, error) {
  63. urlEndpoint, err := url.Parse("https://oapi.dingtalk.com/topapi/v2/user/get")
  64. if err != nil {
  65. return "", "", err
  66. }
  67. query := url.Values{}
  68. query.Set("access_token", d.AccessToken)
  69. urlEndpoint.RawQuery = query.Encode()
  70. urlPath := urlEndpoint.String()
  71. resp, err := http.PostForm(urlPath, url.Values{"userid": {userid}})
  72. if err != nil {
  73. return "", "", err
  74. }
  75. body, err := ioutil.ReadAll(resp.Body)
  76. if err != nil {
  77. return "", "", err
  78. }
  79. // 解析钉钉返回数据
  80. var rdata map[string]interface{}
  81. err = json.Unmarshal(body, &rdata)
  82. if err != nil {
  83. return "", "", err
  84. }
  85. errcode := rdata["errcode"].(float64)
  86. if errcode != 0 {
  87. return "", "", errors.New(fmt.Sprintf("登录错误: %.0f, %s", errcode, rdata["errmsg"].(string)))
  88. }
  89. userinfo := rdata["result"].(map[string]interface{})
  90. username := userinfo["name"].(string)
  91. avatar := userinfo["avatar"].(string)
  92. return username, avatar, nil
  93. }
  94. // GetUserIDByUnionID 根据UnionID获取用户Userid
  95. func (d *DingTalkAgent) GetUserIDByUnionID(unionid string) (string, error) {
  96. urlEndpoint, err := url.Parse("https://oapi.dingtalk.com/topapi/user/getbyunionid")
  97. if err != nil {
  98. return "", err
  99. }
  100. query := url.Values{}
  101. query.Set("access_token", d.AccessToken)
  102. urlEndpoint.RawQuery = query.Encode()
  103. urlPath := urlEndpoint.String()
  104. resp, err := http.PostForm(urlPath, url.Values{"unionid": {unionid}})
  105. if err != nil {
  106. return "", err
  107. }
  108. body, err := ioutil.ReadAll(resp.Body)
  109. if err != nil {
  110. return "", err
  111. }
  112. // 解析钉钉返回数据
  113. var rdata map[string]interface{}
  114. err = json.Unmarshal(body, &rdata)
  115. if err != nil {
  116. return "", err
  117. }
  118. errcode := rdata["errcode"].(float64)
  119. if errcode != 0 {
  120. return "", errors.New(fmt.Sprintf("登录错误: %.0f, %s", errcode, rdata["errmsg"].(string)))
  121. }
  122. result := rdata["result"].(map[string]interface{})
  123. if result["contact_type"].(float64) != 0 {
  124. return "", errors.New("该用户不属于企业内部员工,无法登录。")
  125. }
  126. userid := result["userid"].(string)
  127. return userid, nil
  128. }
  129. // GetAccesstoken 获取钉钉请求Token
  130. func (d *DingTalkAgent) GetAccesstoken() (err error) {
  131. url := fmt.Sprintf("https://oapi.dingtalk.com/gettoken?appkey=%s&appsecret=%s", d.AppKey, d.AppSecret)
  132. resp, err := http.Get(url)
  133. if err != nil {
  134. return err
  135. }
  136. body, err := ioutil.ReadAll(resp.Body)
  137. if err != nil {
  138. return err
  139. }
  140. var i map[string]interface{}
  141. err = json.Unmarshal(body, &i)
  142. if err != nil {
  143. return err
  144. }
  145. if i["errcode"].(float64) == 0 {
  146. d.AccessToken = i["access_token"].(string)
  147. return nil
  148. }
  149. return errors.New("accesstoken获取错误:" + i["errmsg"].(string))
  150. }
  151. // DingtalkQRLogin 用于钉钉扫码登录
  152. type DingtalkQRLogin struct {
  153. AppSecret string
  154. AppKey string
  155. }
  156. // NewDingtalkQRLogin 构造钉钉扫码登录实例
  157. func NewDingtalkQRLogin(appSecret, appKey string) DingtalkQRLogin {
  158. return DingtalkQRLogin{
  159. AppSecret: appSecret,
  160. AppKey: appKey,
  161. }
  162. }
  163. // GetUnionIDByCode 获取扫码用户UnionID
  164. func (d *DingtalkQRLogin) GetUnionIDByCode(code string) (userid string, err error) {
  165. var resp *http.Response
  166. //服务端通过临时授权码获取授权用户的个人信息
  167. timestamp := strconv.FormatInt(time.Now().UnixNano()/1000000, 10) // 毫秒时间戳
  168. signature := d.encodeSHA256(timestamp) // 加密签名
  169. urlPath := fmt.Sprintf(
  170. "https://oapi.dingtalk.com/sns/getuserinfo_bycode?accessKey=%s&timestamp=%s&signature=%s",
  171. d.AppKey, timestamp, signature)
  172. // 构造请求数据
  173. param := struct {
  174. Tmp_auth_code string `json:"tmp_auth_code"`
  175. }{code}
  176. paraByte, _ := json.Marshal(param)
  177. paraString := string(paraByte)
  178. resp, err = http.Post(urlPath, "application/json;charset=UTF-8", strings.NewReader(paraString))
  179. if err != nil {
  180. return "", err
  181. }
  182. body, err := ioutil.ReadAll(resp.Body)
  183. if err != nil {
  184. return "", err
  185. }
  186. // 解析钉钉返回数据
  187. var rdata map[string]interface{}
  188. err = json.Unmarshal(body, &rdata)
  189. if err != nil {
  190. return "", err
  191. }
  192. errcode := rdata["errcode"].(float64)
  193. if errcode != 0 {
  194. return "", errors.New(fmt.Sprintf("登录错误: %.0f, %s", errcode, rdata["errmsg"].(string)))
  195. }
  196. unionid := rdata["user_info"].(map[string]interface{})["unionid"].(string)
  197. return unionid, nil
  198. }
  199. func (d *DingtalkQRLogin) encodeSHA256(timestamp string) string {
  200. // 钉钉签名算法实现
  201. h := hmac.New(sha256.New, []byte(d.AppSecret))
  202. h.Write([]byte(timestamp))
  203. sum := h.Sum(nil) // 二进制流
  204. tmpMsg := base64.StdEncoding.EncodeToString(sum)
  205. uv := url.Values{}
  206. uv.Add("0", tmpMsg)
  207. message := uv.Encode()[2:]
  208. return message
  209. }