mirror of https://github.com/mindoc-org/mindoc.git
增加钉钉自动登录
parent
3ca09b7cbf
commit
a01cb91cbd
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/lifei6671/mindoc/mail"
|
"github.com/lifei6671/mindoc/mail"
|
||||||
"github.com/lifei6671/mindoc/models"
|
"github.com/lifei6671/mindoc/models"
|
||||||
"github.com/lifei6671/mindoc/utils"
|
"github.com/lifei6671/mindoc/utils"
|
||||||
|
"github.com/lifei6671/mindoc/utils/dingtalk"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccountController 用户登录与注册
|
// AccountController 用户登录与注册
|
||||||
|
@ -37,6 +38,7 @@ func (c *AccountController) Prepare() {
|
||||||
c.BaseController.Prepare()
|
c.BaseController.Prepare()
|
||||||
c.EnableXSRF = true
|
c.EnableXSRF = true
|
||||||
c.Data["xsrfdata"] = template.HTML(c.XSRFFormHTML())
|
c.Data["xsrfdata"] = template.HTML(c.XSRFFormHTML())
|
||||||
|
c.Data["corpID"] = beego.AppConfig.String("dingtalk_corpid")
|
||||||
if c.Ctx.Input.IsPost() {
|
if c.Ctx.Input.IsPost() {
|
||||||
token := c.Ctx.Input.Query("_xsrf")
|
token := c.Ctx.Input.Query("_xsrf")
|
||||||
if token == "" {
|
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() {
|
func (c *AccountController) TmpLogin() {
|
||||||
if c.Member != nil {
|
if c.Member != nil {
|
||||||
|
|
|
@ -105,6 +105,17 @@ func (m *Member) Login(account string, password string) (*Member, error) {
|
||||||
return member, ErrorMemberPasswordError
|
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登陆
|
//ldapLogin 通过LDAP登陆
|
||||||
func (m *Member) ldapLogin(account string, password string) (*Member, error) {
|
func (m *Member) ldapLogin(account string, password string) (*Member, error) {
|
||||||
if beego.AppConfig.DefaultBool("ldap_enable", false) == false {
|
if beego.AppConfig.DefaultBool("ldap_enable", false) == false {
|
||||||
|
|
|
@ -10,6 +10,7 @@ func init() {
|
||||||
|
|
||||||
beego.Router("/login", &controllers.AccountController{}, "*:Login")
|
beego.Router("/login", &controllers.AccountController{}, "*:Login")
|
||||||
beego.Router("/token", &controllers.AccountController{}, "get:TmpLogin")
|
beego.Router("/token", &controllers.AccountController{}, "get:TmpLogin")
|
||||||
|
beego.Router("/dingtalk_login", &controllers.AccountController{}, "*:DingTalkLogin")
|
||||||
beego.Router("/logout", &controllers.AccountController{}, "*:Logout")
|
beego.Router("/logout", &controllers.AccountController{}, "*:Logout")
|
||||||
beego.Router("/register", &controllers.AccountController{}, "*:Register")
|
beego.Router("/register", &controllers.AccountController{}, "*:Register")
|
||||||
beego.Router("/find_password", &controllers.AccountController{}, "*:FindPassword")
|
beego.Router("/find_password", &controllers.AccountController{}, "*:FindPassword")
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -87,26 +87,42 @@
|
||||||
<script src="{{cdnjs "/static/bootstrap/js/bootstrap.min.js"}}" type="text/javascript"></script>
|
<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/layer/layer.js"}}" type="text/javascript"></script>
|
||||||
<script src="{{cdnjs "/static/js/dingtalk-jsapi.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">
|
<script type="text/javascript">
|
||||||
if (dd.env.platform !== "notInDingTalk"){
|
if (dd.env.platform !== "notInDingTalk"){
|
||||||
dd.ready(function() {
|
dd.ready(function() {
|
||||||
dd.runtime.permission.requestAuthCode({
|
dd.runtime.permission.requestAuthCode({
|
||||||
corpId: "dingd55b04400e53d11cbc961a6cb783455b", // 企业id
|
corpId: {{ .corpID }} , // 企业id
|
||||||
onSuccess: function (info) {
|
onSuccess: function (info) {
|
||||||
$.post("http://192.168.0.51/token?action=AuthCorpUser", {"code": info.code}, function(rdata){
|
var index = layer.load(1, {
|
||||||
if (rdata.status == 0) {
|
shade: [0.1, '#fff'] // 0.1 透明度的白色背景
|
||||||
$(window).attr('location', rdata.data.url)
|
})
|
||||||
|
|
||||||
}else{
|
var formData = $("form").serializeArray()
|
||||||
alert(rdata.msg)
|
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>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(function () {
|
$(function () {
|
||||||
|
|
Loading…
Reference in New Issue