钉钉的的官方开发文档中只给出了java实现三方登录的,我们准备用go语言来实现
实现网页方式登录应用(登录第三方网站) - 钉钉开放平台
首先就是按照文档进行操作,备注好网站的信息
获得应用凭证,我们后面会用到
之后配置回调域名,用于后续前端页面重定向使用
开通用户的个人信息权限
代码如下
控制层
// RedirectToDingTalkLogin
// @Description: 将用户重定向到钉钉登录授权页
// @param c *gin.Context
func RedirectToDingTalkLogin(c *gin.Context) {
appId := "**这里写你的Client ID**"
redirectUri := url.QueryEscape("**这里写你的回调地址**") // 钉钉回调地址
state := "random-state" //防止CSRF,可选
url := fmt.Sprintf("https://oapi.dingtalk.com/connect/qrconnect?appid=%s&response_type=code&scope=snsapi_login&state=%s&redirect_uri=%s",
appId, state, redirectUri)
c.Redirect(http.StatusFound, url)
}
// GetDingTalkToken
// @Description: 处理钉钉回调信息
// @param c *gin.Context
func GetDingTalkToken(c *gin.Context) {
authCode := c.DefaultQuery("authCode", "")
if authCode == "" {
response.Failed(c, http.StatusBadRequest, response.NewAppErr(globals.StatusBadRequest, nil, nil))
return
}
accessToken, err := logics.GetAccessToken(authCode)
if err != nil {
response.Failed(c, http.StatusInternalServerError, response.NewAppErr(globals.StatusInternalServerError, err, nil))
return
}
//获取用户信息
userInfo, err := logics.GetDingTalkUserInfo(accessToken)
if err != nil {
response.Failed(c, http.StatusInternalServerError, response.NewAppErr(globals.StatusInternalServerError, err, nil))
return
}
logics.ThirdPartLogin(c, userInfo, "dingTalk")
}
业务层代码
var dingTalkConf = requests.Conf{
ClientId: "你的client ID",
ClientSecret: "你的ClientSecret",
RedirectUrl: "你的回调地址",
}
// GetAccessToken
// @Description: 获取钉钉通行token
// @param authCode string
// @return string
// @return error
func GetAccessToken(authCode string) (string, error) {
api := "https://api.dingtalk.com/v1.0/oauth2/userAccessToken"
payload := url.Values{}
payload.Set("clientId", dingTalkConf.ClientId)
payload.Set("clientSecret", dingTalkConf.ClientSecret)
payload.Set("code", authCode)
payload.Set("grantType", "authorization_code")
resp, err := http.PostForm(api, payload)
if err != nil {
return "", fmt.Errorf("post token error: %v", err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("failed to get token: %s", string(body))
}
var tokenResp requests.TokenResponse
if err = json.Unmarshal(body, &tokenResp); err != nil {
return "", fmt.Errorf("parse token json error: %v", err)
}
return tokenResp.AccessToken, nil
}
// GetDingTalkUserInfo
// @Description: 通过token获取用户信息
// @param accessToken string
// @return *models.User
// @return error
func GetDingTalkUserInfo(accessToken string) (map[string]interface{}, error) {
api := "https://api.dingtalk.com/v1.0/contact/users/me"
req, _ := http.NewRequest("GET", api, nil)
req.Header.Set("x-acs-dingtalk-access-token", accessToken)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("get user info error: %v", err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to get user info: %s", string(body))
}
var userInfo map[string]interface{}
if err = json.Unmarshal(body, &userInfo); err != nil {
return nil, fmt.Errorf("parse user json error: %v", err)
}
return userInfo, nil
}
// ThirdPartLogin
// @Description: 根据第三方信息登录并生成token
// @param c *gin.Context
// @param userInfo map[string]interface{}
// @param provider string
func ThirdPartLogin(c *gin.Context, userInfo map[string]interface{}, provider string) {
db := globals.DB
//判断用户是否扫码登录过
userId, err := repositories.CheckHistoryLogin(db, userInfo["id"].(string), provider)
if err != nil {
response.Failed(c, http.StatusInternalServerError, response.NewAppErr(globals.StatusInternalServerError, err, nil))
}
if userId == 0 {
//判断用户是否注册过
user := repositories.QueryUserByEmail(db, userInfo["email"].(string))
if user == nil {
//没注册过直接跳转至注册
return
}
//将三方登录记录插入记录表
err = repositories.InsertOtherLogin(db, userInfo["id"].(string), provider, user.ID)
if err != nil {
response.Failed(c, http.StatusInternalServerError, response.NewAppErr(globals.StatusInternalServerError, err, nil))
return
}
}
// 生成token
id := userId
tok, err := token.GenerateToken(id, userInfo["email"].(string))
if err != nil {
globals.Log.Errorf(err.Error())
response.Failed(c, http.StatusInternalServerError, response.NewAppErr(globals.StatusInternalServerError, err, nil))
return
}
response.Success(c, http.StatusOK, response.NewAppData(globals.StatusOK, response.DataSuccess, gin.H{"Token": tok, "userinfo": userInfo}))
}