mirror of https://github.com/mindoc-org/mindoc.git
backend for dingtalk QR login
parent
0c8901ea05
commit
9b94192d99
|
@ -191,6 +191,81 @@ func (c *AccountController) DingTalkLogin() {
|
|||
c.JsonResult(0, "ok", username)
|
||||
}
|
||||
|
||||
// QR二维码登录
|
||||
func (c *AccountController) QRLogin() {
|
||||
c.Prepare()
|
||||
|
||||
appName := c.Ctx.Input.Param(":app")
|
||||
|
||||
switch appName {
|
||||
// 钉钉扫码登录
|
||||
case "dingtalk":
|
||||
code := c.GetString("code")
|
||||
state := c.GetString("state")
|
||||
if state != "1" || code == "" {
|
||||
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||
c.StopRun()
|
||||
}
|
||||
appKey := "dingoa0wp8qg6gyqtlyno1"
|
||||
appSecret := "YPcijr8Wj47_N2jjEtE3wUsfiUwcTJqfJewgTs9mnHmaHAIsxAe92fmzy-XpdSMs"
|
||||
|
||||
qrDingtalk := dingtalk.NewDingtalkQRLogin(appSecret, appKey)
|
||||
unionID, err := qrDingtalk.GetUnionIDByCode(code)
|
||||
if err != nil {
|
||||
beego.Warn("获取钉钉临时Token失败 ->", err)
|
||||
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||
c.StopRun()
|
||||
}
|
||||
|
||||
appKey = beego.AppConfig.String("dingtalk_app_key")
|
||||
appSecret = beego.AppConfig.String("dingtalk_app_secret")
|
||||
tmpReader := beego.AppConfig.String("dingtalk_tmp_reader")
|
||||
|
||||
dingtalkAgent := dingtalk.NewDingTalkAgent(appSecret, appKey)
|
||||
err = dingtalkAgent.GetAccesstoken()
|
||||
if err != nil {
|
||||
beego.Warn("获取钉钉临时Token失败 ->", err)
|
||||
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||
c.StopRun()
|
||||
}
|
||||
|
||||
userid, err := dingtalkAgent.GetUserIDByUnionID(unionID)
|
||||
if err != nil {
|
||||
beego.Warn("获取钉钉临时Token失败 ->", err)
|
||||
c.StopRun()
|
||||
}
|
||||
|
||||
username, avatar, err := dingtalkAgent.GetUserNameAndAvatarByUserID(userid)
|
||||
if err != nil {
|
||||
beego.Warn("钉钉自动登录失败 ->", err)
|
||||
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||
c.StopRun()
|
||||
}
|
||||
|
||||
member, err := models.NewMember().TmpLogin(tmpReader)
|
||||
if err == nil {
|
||||
member.LastLoginTime = time.Now()
|
||||
_ = member.Update("last_login_time")
|
||||
member.Account = username
|
||||
if avatar != "" {
|
||||
member.Avatar = avatar
|
||||
}
|
||||
|
||||
c.SetMember(*member)
|
||||
c.LoggedIn(false)
|
||||
c.StopRun()
|
||||
}
|
||||
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||
|
||||
// fmt.Println(unionID)
|
||||
// c.JsonResult(0, appName, nil)
|
||||
|
||||
default:
|
||||
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||
c.StopRun()
|
||||
}
|
||||
}
|
||||
|
||||
// 登录成功后的操作,如重定向到原始请求页面
|
||||
func (c *AccountController) LoggedIn(isPost bool) interface{} {
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ func init() {
|
|||
|
||||
beego.Router("/login", &controllers.AccountController{}, "*:Login")
|
||||
beego.Router("/dingtalk_login", &controllers.AccountController{}, "*:DingTalkLogin")
|
||||
beego.Router("/qrlogin/:app", &controllers.AccountController{}, "*:QRLogin")
|
||||
beego.Router("/logout", &controllers.AccountController{}, "*:Logout")
|
||||
beego.Router("/register", &controllers.AccountController{}, "*:Register")
|
||||
beego.Router("/find_password", &controllers.AccountController{}, "*:FindPassword")
|
||||
|
|
|
@ -10,6 +10,9 @@ import (
|
|||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DingTalkAgent 用于钉钉交互
|
||||
|
@ -106,6 +109,46 @@ func (d *DingTalkAgent) GetUserNameAndAvatarByUserID(userid string) (string, str
|
|||
return username, avatar, nil
|
||||
}
|
||||
|
||||
// GetUserIDByUnionID 根据UnionID获取用户Userid
|
||||
func (d *DingTalkAgent) GetUserIDByUnionID(unionid string) (string, error) {
|
||||
urlEndpoint, err := url.Parse("https://oapi.dingtalk.com/topapi/user/getbyunionid")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
query := url.Values{}
|
||||
query.Set("access_token", d.AccessToken)
|
||||
urlEndpoint.RawQuery = query.Encode()
|
||||
urlPath := urlEndpoint.String()
|
||||
|
||||
resp, err := http.PostForm(urlPath, url.Values{"unionid": {unionid}})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 解析钉钉返回数据
|
||||
var rdata map[string]interface{}
|
||||
err = json.Unmarshal(body, &rdata)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
errcode := rdata["errcode"].(float64)
|
||||
if errcode != 0 {
|
||||
return "", errors.New(fmt.Sprintf("登录错误: %.0f, %s", errcode, rdata["errmsg"].(string)))
|
||||
}
|
||||
|
||||
result := rdata["result"].(map[string]interface{})
|
||||
if result["contact_type"].(float64) != 0 {
|
||||
return "", errors.New("该用户不属于企业内部员工,无法登录。")
|
||||
}
|
||||
userid := result["userid"].(string)
|
||||
return userid, nil
|
||||
}
|
||||
|
||||
// GetAccesstoken 获取钉钉请求Token
|
||||
func (d *DingTalkAgent) GetAccesstoken() (err error) {
|
||||
|
||||
|
@ -132,16 +175,71 @@ func (d *DingTalkAgent) GetAccesstoken() (err error) {
|
|||
return errors.New("accesstoken获取错误:" + i["errmsg"].(string))
|
||||
}
|
||||
|
||||
func (d *DingTalkAgent) encodeSHA256(message string) string {
|
||||
// DingtalkQRLogin 用于钉钉扫码登录
|
||||
type DingtalkQRLogin struct {
|
||||
AppSecret string
|
||||
AppKey string
|
||||
}
|
||||
|
||||
// NewDingtalkQRLogin 构造钉钉扫码登录实例
|
||||
func NewDingtalkQRLogin(appSecret, appKey string) DingtalkQRLogin {
|
||||
return DingtalkQRLogin{
|
||||
AppSecret: appSecret,
|
||||
AppKey: appKey,
|
||||
}
|
||||
}
|
||||
|
||||
// GetUnionIDByCode 获取扫码用户UnionID
|
||||
func (d *DingtalkQRLogin) GetUnionIDByCode(code string) (userid string, err error) {
|
||||
var resp *http.Response
|
||||
//服务端通过临时授权码获取授权用户的个人信息
|
||||
timestamp := strconv.FormatInt(time.Now().UnixNano()/1000000, 10) // 毫秒时间戳
|
||||
signature := d.encodeSHA256(timestamp) // 加密签名
|
||||
urlPath := fmt.Sprintf(
|
||||
"https://oapi.dingtalk.com/sns/getuserinfo_bycode?accessKey=%s×tamp=%s&signature=%s",
|
||||
d.AppKey, timestamp, signature)
|
||||
|
||||
// 构造请求数据
|
||||
param := struct {
|
||||
Tmp_auth_code string `json:"tmp_auth_code"`
|
||||
}{code}
|
||||
paraByte, _ := json.Marshal(param)
|
||||
paraString := string(paraByte)
|
||||
|
||||
resp, err = http.Post(urlPath, "application/json;charset=UTF-8", strings.NewReader(paraString))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 解析钉钉返回数据
|
||||
var rdata map[string]interface{}
|
||||
err = json.Unmarshal(body, &rdata)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
errcode := rdata["errcode"].(float64)
|
||||
if errcode != 0 {
|
||||
return "", errors.New(fmt.Sprintf("登录错误: %.0f, %s", errcode, rdata["errmsg"].(string)))
|
||||
}
|
||||
unionid := rdata["user_info"].(map[string]interface{})["unionid"].(string)
|
||||
return unionid, nil
|
||||
}
|
||||
|
||||
func (d *DingtalkQRLogin) encodeSHA256(timestamp string) string {
|
||||
// 钉钉签名算法实现
|
||||
h := hmac.New(sha256.New, []byte(d.AppSecret))
|
||||
h.Write([]byte(message))
|
||||
h.Write([]byte(timestamp))
|
||||
sum := h.Sum(nil) // 二进制流
|
||||
tmpMsg := base64.StdEncoding.EncodeToString(sum)
|
||||
|
||||
uv := url.Values{}
|
||||
uv.Add("0", tmpMsg)
|
||||
message = uv.Encode()[2:]
|
||||
message := uv.Encode()[2:]
|
||||
|
||||
return message
|
||||
}
|
||||
|
|
|
@ -70,6 +70,10 @@
|
|||
<div class="form-group">
|
||||
<button type="button" id="btn-login" class="btn btn-success" style="width: 100%" data-loading-text="正在登录..." autocomplete="off">立即登录</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<a href='https://oapi.dingtalk.com/connect/qrconnect?appid=dingoa0wp8qg6gyqtlyno1&response_type=code&scope=snsapi_login&state=1&redirect_uri={{ urlfor "AccountController.QRLogin" ":app" "dingtalk"}}' id="btn-dingtalk-qr" class="btn btn-default" style="width: 100%" data-loading-text="" autocomplete="off">钉钉扫码登录</a>
|
||||
<!-- <a href='{{ urlfor "AccountController.QRLogin" ":app" "dingtalk"}}' id="btn-dingtalk-qr" class="btn btn-default" style="width: 100%" data-loading-text="" autocomplete="off">钉钉扫码登录</a> -->
|
||||
</div>
|
||||
{{if .ENABLED_REGISTER}}
|
||||
{{if ne .ENABLED_REGISTER "false"}}
|
||||
<div class="form-group">
|
||||
|
|
Loading…
Reference in New Issue