浏览代码

增加钉钉自动登录

LawyZHENG 4 年之前
父节点
当前提交
a01cb91cbd
共有 5 个文件被更改,包括 190 次插入9 次删除
  1. 46 0
      controllers/AccountController.go
  2. 11 0
      models/Member.go
  3. 1 0
      routers/router.go
  4. 107 0
      utils/dingtalk/dingtalk.go
  5. 25 9
      views/account/login.tpl

+ 46 - 0
controllers/AccountController.go

@@ -18,6 +18,7 @@ import (
 	"github.com/lifei6671/mindoc/mail"
 	"github.com/lifei6671/mindoc/models"
 	"github.com/lifei6671/mindoc/utils"
+	"github.com/lifei6671/mindoc/utils/dingtalk"
 )
 
 // AccountController 用户登录与注册
@@ -37,6 +38,7 @@ func (c *AccountController) Prepare() {
 	c.BaseController.Prepare()
 	c.EnableXSRF = true
 	c.Data["xsrfdata"] = template.HTML(c.XSRFFormHTML())
+	c.Data["corpID"] = beego.AppConfig.String("dingtalk_corpid")
 	if c.Ctx.Input.IsPost() {
 		token := c.Ctx.Input.Query("_xsrf")
 		if token == "" {
@@ -136,6 +138,50 @@ func (c *AccountController) Login() {
 	}
 }
 
+// 钉钉登录
+func (c *AccountController) DingTalkLogin() {
+	c.Prepare()
+
+	code := c.GetString("code")
+	if code == "" {
+		c.Redirect(conf.URLFor("AccountController.Login"), 302)
+	}
+
+	appKey := beego.AppConfig.String("dingtalk_app_key")
+	appSecret := beego.AppConfig.String("dingtalk_app_secret")
+	tmpReader := beego.AppConfig.String("dingtalk_tmp_reader")
+
+	if appKey == "" || appSecret == "" || tmpReader == "" {
+		c.JsonResult(500, "未开启钉钉自动登录功能", nil)
+		c.StopRun()
+	}
+
+	dingtalkAgent := dingtalk.NewDingTalkAgent(appSecret, appKey)
+	err := dingtalkAgent.GetAccesstoken()
+	if err != nil {
+		beego.Warn("获取钉钉临时Token失败 ->", err)
+		c.JsonResult(500, "自动登录失败", nil)
+		c.StopRun()
+	}
+
+	username, err := dingtalkAgent.GetUserNameByCode(code)
+	if err != nil {
+		beego.Warn("钉钉自动登录失败 ->", err)
+		c.JsonResult(500, "自动登录失败", nil)
+		c.StopRun()
+	}
+
+	member, err := models.NewMember().TmpLogin(tmpReader)
+	if err == nil {
+		member.LastLoginTime = time.Now()
+		_ = member.Update("last_login_time")
+
+		c.SetMember(*member)
+		c.LoggedIn(false)
+	}
+	c.JsonResult(0, "ok", username)
+}
+
 // 临时登录
 func (c *AccountController) TmpLogin() {
 	if c.Member != nil {

+ 11 - 0
models/Member.go

@@ -105,6 +105,17 @@ func (m *Member) Login(account string, password string) (*Member, error) {
 	return member, ErrorMemberPasswordError
 }
 
+// TmpLogin 用于钉钉临时登录
+func (m *Member) TmpLogin(account string) (*Member, error) {
+	o := orm.NewOrm()
+	member := &Member{}
+	err := o.Raw("select * from md_members where account = ? and status = 0 limit 1;", account).QueryRow(member)
+	if err != nil {
+		return member, ErrorMemberPasswordError
+	}
+	return member, nil
+}
+
 //ldapLogin 通过LDAP登陆
 func (m *Member) ldapLogin(account string, password string) (*Member, error) {
 	if beego.AppConfig.DefaultBool("ldap_enable", false) == false {

+ 1 - 0
routers/router.go

@@ -10,6 +10,7 @@ func init() {
 
 	beego.Router("/login", &controllers.AccountController{}, "*:Login")
 	beego.Router("/token", &controllers.AccountController{}, "get:TmpLogin")
+	beego.Router("/dingtalk_login", &controllers.AccountController{}, "*:DingTalkLogin")
 	beego.Router("/logout", &controllers.AccountController{}, "*:Logout")
 	beego.Router("/register", &controllers.AccountController{}, "*:Register")
 	beego.Router("/find_password", &controllers.AccountController{}, "*:FindPassword")

+ 107 - 0
utils/dingtalk/dingtalk.go

@@ -0,0 +1,107 @@
+package dingtalk
+
+import (
+	"crypto/hmac"
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+)
+
+// DingTalkAgent 用于钉钉交互
+type DingTalkAgent struct {
+	AppSecret   string
+	AppKey      string
+	AccessToken string
+}
+
+// NewDingTalkAgent 钉钉交互构造函数
+func NewDingTalkAgent(appSecret, appKey string) *DingTalkAgent {
+	return &DingTalkAgent{
+		AppSecret: appSecret,
+		AppKey:    appKey,
+	}
+}
+
+// GetUserNameByCode 通过临时code获取当前用户信息
+func (d *DingTalkAgent) GetUserNameByCode(code string) (string, error) {
+	urlEndpoint, err := url.Parse("https://oapi.dingtalk.com/user/getuserinfo")
+	if err != nil {
+		return "", err
+	}
+
+	query := url.Values{}
+	query.Set("access_token", d.AccessToken)
+	query.Set("code", code)
+
+	urlEndpoint.RawQuery = query.Encode()
+	urlPath := urlEndpoint.String()
+
+	resp, err := http.Get(urlPath)
+	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)))
+	}
+
+	username := rdata["name"].(string)
+	return username, nil
+}
+
+// GetAccesstoken 获取钉钉请求Token
+func (d *DingTalkAgent) GetAccesstoken() (err error) {
+
+	url := fmt.Sprintf("https://oapi.dingtalk.com/gettoken?appkey=%s&appsecret=%s", d.AppKey, d.AppSecret)
+	resp, err := http.Get(url)
+	if err != nil {
+		return err
+	}
+	body, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return err
+	}
+
+	var i map[string]interface{}
+	err = json.Unmarshal(body, &i)
+	if err != nil {
+		return err
+	}
+
+	if i["errcode"].(float64) == 0 {
+		d.AccessToken = i["access_token"].(string)
+		return nil
+	}
+	return errors.New("accesstoken获取错误:" + i["errmsg"].(string))
+}
+
+func (d *DingTalkAgent) encodeSHA256(message string) string {
+	// 钉钉签名算法实现
+	h := hmac.New(sha256.New, []byte(d.AppSecret))
+	h.Write([]byte(message))
+	sum := h.Sum(nil) // 二进制流
+	tmpMsg := base64.StdEncoding.EncodeToString(sum)
+
+	uv := url.Values{}
+	uv.Add("0", tmpMsg)
+	message = uv.Encode()[2:]
+
+	return message
+}

+ 25 - 9
views/account/login.tpl

@@ -87,26 +87,42 @@
 <script src="{{cdnjs "/static/bootstrap/js/bootstrap.min.js"}}" type="text/javascript"></script>
 <script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript"></script>
 <script src="{{cdnjs "/static/js/dingtalk-jsapi.js"}}" type="text/javascript"></script>
-<!-- <script src="https://g.alicdn.com/dingding/dingtalk-jsapi/2.10.3/dingtalk.open.js"></script> -->
 <script type="text/javascript">
     if (dd.env.platform !== "notInDingTalk"){
         dd.ready(function() {
             dd.runtime.permission.requestAuthCode({
-                corpId: "dingd55b04400e53d11cbc961a6cb783455b", // 企业id
+                corpId: {{ .corpID }} , // 企业id
                 onSuccess: function (info) {
-                    $.post("http://192.168.0.51/token?action=AuthCorpUser", {"code": info.code}, function(rdata){
-                        if (rdata.status == 0) {
-                            $(window).attr('location', rdata.data.url)
+                    var index = layer.load(1, {
+                        shade: [0.1, '#fff'] // 0.1 透明度的白色背景
+                    })
 
-                        }else{
-                            alert(rdata.msg)
-                        }
+                    var formData = $("form").serializeArray()
+                    formData.push({"name": "code", "value": info.code})
+
+                    $.ajax({
+                        url: "{{urlfor "AccountController.DingTalkLogin"}} ",
+                        data: formData,
+                        dataType: "json",
+                        type: "POST",
+                        complete: function(){
+                            layer.close(index)
+                        },
+                        success: function (res) {
+                            if (res.errcode !== 0) {
+                                layer.msg(res.message)
+                            } else {
+                                window.location = "/"
+                            }
+                        },
+                        // error: function () {
+                        // }
                     })
-                //    alert(info.code) // 通过该免登授权码可以获取用户身份
                 }
             });
         });
     }
+
 </script>
 <script type="text/javascript">
     $(function () {