From a01cb91cbd826686c3da88adb06a1ba8a44bd1b8 Mon Sep 17 00:00:00 2001 From: LawyZHENG Date: Fri, 19 Mar 2021 10:33:59 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=92=89=E9=92=89=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/AccountController.go | 46 +++++++++++++ models/Member.go | 11 ++++ routers/router.go | 1 + utils/dingtalk/dingtalk.go | 107 +++++++++++++++++++++++++++++++ views/account/login.tpl | 36 ++++++++--- 5 files changed, 191 insertions(+), 10 deletions(-) create mode 100644 utils/dingtalk/dingtalk.go diff --git a/controllers/AccountController.go b/controllers/AccountController.go index 18c78a55..4cfb1e77 100644 --- a/controllers/AccountController.go +++ b/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 { diff --git a/models/Member.go b/models/Member.go index 0f34d28f..929e7491 100644 --- a/models/Member.go +++ b/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 { diff --git a/routers/router.go b/routers/router.go index 441c1274..7aedf1bf 100644 --- a/routers/router.go +++ b/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") diff --git a/utils/dingtalk/dingtalk.go b/utils/dingtalk/dingtalk.go new file mode 100644 index 00000000..b4631dee --- /dev/null +++ b/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 +} diff --git a/views/account/login.tpl b/views/account/login.tpl index b47b98fc..7436f2e8 100644 --- a/views/account/login.tpl +++ b/views/account/login.tpl @@ -87,26 +87,42 @@ -