Browse Source

企业微信8.22之后根据用户id获取信息已停用,修改登录认证

code huan 3 years ago
parent
commit
b9510a7057
2 changed files with 257 additions and 188 deletions
  1. 18 14
      controllers/AccountController.go
  2. 239 174
      utils/workweixin/workweixin.go

+ 18 - 14
controllers/AccountController.go

@@ -406,7 +406,10 @@ func (c *AccountController) WorkWeixinLoginCallback() {
 				contact_access_token, ok := workweixin.GetAccessToken(true)
 				if ok {
 					logs.Warning("contact_access_token: ", contact_access_token)
-					user_info, err_msg, ok := workweixin.RequestUserInfo(contact_access_token, user_id)
+					// 获取用户信息
+					//user_info, err_msg, ok := workweixin.RequestUserInfo(contact_access_token, user_id)
+					// 获取用户id 列表
+					user_info, err_msg, ok := workweixin.GetUserListId(contact_access_token, user_id)
 					if ok {
 						// [-------所有字段-Debug----------
 						// user_info.UserId
@@ -502,7 +505,7 @@ func (c *AccountController) WorkWeixinLoginCallback() {
 
 // WorkWeixinLoginBind 用户企业微信登录-绑定
 func (c *AccountController) WorkWeixinLoginBind() {
-	if user_info, ok := c.GetSession(SessionUserInfoKey).(workweixin.WorkWeixinUserInfo); ok && len(user_info.UserId) > 0 {
+	if user_info, ok := c.GetSession(SessionUserInfoKey).(workweixin.WorkWeixinDeptUserInfo); ok && len(user_info.UserId) > 0 {
 		req_account := c.GetString("account")
 		req_password := c.GetString("password")
 		if req_account == "" || req_password == "" {
@@ -517,29 +520,30 @@ func (c *AccountController) WorkWeixinLoginBind() {
 				ormer := orm.NewOrm()
 				o, err := ormer.Begin()
 				if err != nil {
-					logs.Error("开启事时出错 -> ", err)
-					c.JsonResult(500, "开启事时出错: ", err.Error())
+					logs.Error("开启事时出错 -> ", err)
+					c.JsonResult(500, "开启事时出错: ", err.Error())
 				}
 				if err := account.AddBind(ormer); err != nil {
 					o.Rollback()
 					c.JsonResult(500, "绑定失败,数据库错误: "+err.Error())
 				} else {
+					// 绑定成功之后修改用户信息
 					member.LastLoginTime = time.Now()
-					member.RealName = user_info.Name
-					member.Avatar = user_info.Avatar
+					//member.RealName = user_info.Name
+					//member.Avatar = user_info.Avatar
 					if len(member.Avatar) < 1 {
 						member.Avatar = conf.GetDefaultAvatar()
 					}
-					member.Email = user_info.Email
-					member.Phone = user_info.Mobile
+					//member.Email = user_info.Email
+					//member.Phone = user_info.Mobile
 					if _, err := ormer.Update(member, "last_login_time", "real_name", "avatar", "email", "phone"); err != nil {
 						o.Rollback()
 						logs.Error("保存用户信息失败=>", err)
 						c.JsonResult(500, "绑定失败,现有账户信息更新失败: "+err.Error())
 					} else {
 						if err := o.Commit(); err != nil {
-							logs.Error("提交事物时出错 -> ", err)
-							c.JsonResult(500, "提交事物时出错: ", err.Error())
+							logs.Error("开启事务时出错 -> ", err)
+							c.JsonResult(500, "开启事务时出错: ", err.Error())
 						} else {
 							c.DelSession(SessionUserInfoKey)
 							c.SetMember(*member)
@@ -588,8 +592,8 @@ func (c *AccountController) WorkWeixinLoginIgnore() {
 		ormer := orm.NewOrm()
 		o, err := ormer.Begin()
 		if err != nil {
-			logs.Error("开启事时出错 -> ", err)
-			c.JsonResult(500, "开启事时出错: ", err.Error())
+			logs.Error("开启事时出错 -> ", err)
+			c.JsonResult(500, "开启事时出错: ", err.Error())
 		}
 
 		member.Account = user_info.UserId
@@ -630,8 +634,8 @@ func (c *AccountController) WorkWeixinLoginIgnore() {
 				c.JsonResult(500, "注册失败,数据库错误: "+err.Error())
 			} else {
 				if err := o.Commit(); err != nil {
-					logs.Error("提交事时出错 -> ", err)
-					c.JsonResult(500, "提交事时出错: ", err.Error())
+					logs.Error("提交事时出错 -> ", err)
+					c.JsonResult(500, "提交事时出错: ", err.Error())
 				} else {
 					member.LastLoginTime = time.Now()
 					_ = member.Update("last_login_time")

+ 239 - 174
utils/workweixin/workweixin.go

@@ -1,222 +1,287 @@
 package workweixin
 
 import (
-    "context"
-    "crypto/tls"
-    // "encoding/json"
-    "net/http"
-    "time"
-
-    "github.com/beego/beego/v2/client/httplib"
-    "github.com/beego/beego/v2/core/logs"
-    "github.com/mindoc-org/mindoc/cache"
-    "github.com/mindoc-org/mindoc/conf"
+	"context"
+	"crypto/tls"
+	// "encoding/json"
+	"net/http"
+	"time"
+
+	"github.com/beego/beego/v2/client/httplib"
+	"github.com/beego/beego/v2/core/logs"
+	"github.com/mindoc-org/mindoc/cache"
+	"github.com/mindoc-org/mindoc/conf"
 )
 
 // doc
 // - 全局错误码: https://work.weixin.qq.com/api/doc/90000/90139/90313
 
 const (
-    AccessTokenCacheKey        = "access-token-cache-key"
-    ContactAccessTokenCacheKey = "contact-access-token-cache-key"
+	AccessTokenCacheKey        = "access-token-cache-key"
+	ContactAccessTokenCacheKey = "contact-access-token-cache-key"
 )
 
 // 获取访问凭据-请求响应结构
 type AccessTokenResponse struct {
-    ErrCode     int    `json:"errcode"`
-    ErrMsg      string `json:"errmsg"`
-    AccessToken string `json:"access_token"` // 获取到的凭证,最长为512字节
-    ExpiresIn   int    `json:"expires_in"`   // 凭证的有效时间(秒)
+	ErrCode     int    `json:"errcode"`
+	ErrMsg      string `json:"errmsg"`
+	AccessToken string `json:"access_token"` // 获取到的凭证,最长为512字节
+	ExpiresIn   int    `json:"expires_in"`   // 凭证的有效时间(秒)
 }
 
 // 获取用户Id-请求响应结构
 type UserIdResponse struct {
-    ErrCode  int    `json:"errcode"`
-    ErrMsg   string `json:"errmsg"`
-    UserId   string `json:"UserId"`   // 企业成员UserID
-    OpenId   string `json:"OpenId"`   // 非企业成员的标识,对当前企业唯一
-    DeviceId string `json:"DeviceId"` // 设备号
+	ErrCode  int    `json:"errcode"`
+	ErrMsg   string `json:"errmsg"`
+	UserId   string `json:"UserId"`   // 企业成员UserID
+	OpenId   string `json:"OpenId"`   // 非企业成员的标识,对当前企业唯一
+	DeviceId string `json:"DeviceId"` // 设备号
+}
+
+// 获取成员ID列表-请求响应结构
+type UserListIdResponse struct {
+	ErrCode    int                      `json:"errcode"`
+	ErrMsg     string                   `json:"errmsg"`
+	NextCursor string                   `json:"next_cursor"` // 分页游标,下次请求时填写以获取之后分页的记录
+	DeptUser   []WorkWeixinDeptUserInfo `json:"dept_user"`   // 用户-部门关系列表
+
 }
 
 // 获取用户信息-请求响应结构
 type UserInfoResponse struct {
-    ErrCode        int    `json:"errcode"`
-    ErrMsg         string `json:"errmsg"`
-    UserId         string `json:"UserId"`            // 企业成员UserID
-    Name           string `json:"name"`              // 成员名称
-    HideMobile     int    `json:"hide_mobile"`       // 是否隐藏了手机号码
-    Mobile         string `json:"mobile"`            // 手机号码
-    Department     []int  `json:"department"`        // 成员所属部门id列表
-    Email          string `json:"email"`             // 邮箱
-    IsLeaderInDept []int  `json:"is_leader_in_dept"` // 表示在所在的部门内是否为上级
-    IsLeader       int    `json:"isleader"`          // 是否是部门上级(领导)
-    Avatar         string `json:"avatar"`            // 头像url
-    Alias          string `json:"alias"`             // 别名
-    Status         int    `json:"status"`            // 激活状态: 1=已激活,2=已禁用,4=未激活,5=退出企业
-    MainDepartment int    `json:"main_department"`   // 主部门
+	ErrCode        int    `json:"errcode"`
+	ErrMsg         string `json:"errmsg"`
+	UserId         string `json:"UserId"`            // 企业成员UserID
+	Name           string `json:"name"`              // 成员名称
+	HideMobile     int    `json:"hide_mobile"`       // 是否隐藏了手机号码
+	Mobile         string `json:"mobile"`            // 手机号码
+	Department     []int  `json:"department"`        // 成员所属部门id列表
+	Email          string `json:"email"`             // 邮箱
+	IsLeaderInDept []int  `json:"is_leader_in_dept"` // 表示在所在的部门内是否为上级
+	IsLeader       int    `json:"isleader"`          // 是否是部门上级(领导)
+	Avatar         string `json:"avatar"`            // 头像url
+	Alias          string `json:"alias"`             // 别名
+	Status         int    `json:"status"`            // 激活状态: 1=已激活,2=已禁用,4=未激活,5=退出企业
+	MainDepartment int    `json:"main_department"`   // 主部门
 }
 
 // 访问凭据缓存-结构
 type AccessTokenCache struct {
-    AccessToken string    `json:"access_token"`
-    ExpiresIn   int       `json:"expires_in"`
-    UpdateTime  time.Time `json:"update_time"`
+	AccessToken string    `json:"access_token"`
+	ExpiresIn   int       `json:"expires_in"`
+	UpdateTime  time.Time `json:"update_time"`
+}
+
+// 企业微信用户信息-结构
+type WorkWeixinDeptUserInfo struct {
+	UserId     string `json:"UserId"`     // 企业成员UserID
+	Department int    `json:"department"` // 成员所属部门id列表
 }
 
 // 企业微信用户信息-结构
 type WorkWeixinUserInfo struct {
-    UserId         string `json:"UserId"`            // 企业成员UserID
-    Name           string `json:"name"`              // 成员名称
-    HideMobile     int    `json:"hide_mobile"`       // 是否隐藏了手机号码
-    Mobile         string `json:"mobile"`            // 手机号码
-    Department     []int  `json:"department"`        // 成员所属部门id列表
-    Email          string `json:"email"`             // 邮箱
-    IsLeaderInDept []int  `json:"is_leader_in_dept"` // 表示在所在的部门内是否为上级
-    IsLeader       int    `json:"isleader"`          // 是否是部门上级(领导)
-    Avatar         string `json:"avatar"`            // 头像url
-    Alias          string `json:"alias"`             // 别名
-    Status         int    `json:"status"`            // 激活状态: 1=已激活,2=已禁用,4=未激活,5=退出企业
-    MainDepartment int    `json:"main_department"`   // 主部门
+	UserId         string `json:"UserId"`            // 企业成员UserID
+	Name           string `json:"name"`              // 成员名称
+	HideMobile     int    `json:"hide_mobile"`       // 是否隐藏了手机号码
+	Mobile         string `json:"mobile"`            // 手机号码
+	Department     []int  `json:"department"`        // 成员所属部门id列表
+	Email          string `json:"email"`             // 邮箱
+	IsLeaderInDept []int  `json:"is_leader_in_dept"` // 表示在所在的部门内是否为上级
+	IsLeader       int    `json:"isleader"`          // 是否是部门上级(领导)
+	Avatar         string `json:"avatar"`            // 头像url
+	Alias          string `json:"alias"`             // 别名
+	Status         int    `json:"status"`            // 激活状态: 1=已激活,2=已禁用,4=未激活,5=退出企业
+	MainDepartment int    `json:"main_department"`   // 主部门
 }
 
 func httpFilter(next httplib.Filter) httplib.Filter {
-    return func(ctx context.Context, req *httplib.BeegoHTTPRequest) (*http.Response, error) {
-        r := req.GetRequest()
-        logs.Info("filter-url: ", r.URL)
-        // Never forget invoke this. Or the request will not be sent
-        return next(ctx, req)
-    }
+	return func(ctx context.Context, req *httplib.BeegoHTTPRequest) (*http.Response, error) {
+		r := req.GetRequest()
+		logs.Info("filter-url: ", r.URL)
+		// Never forget invoke this. Or the request will not be sent
+		return next(ctx, req)
+	}
 }
 
 // 获取访问凭据-请求
 func RequestAccessToken(corpid string, secret string) (cache_token AccessTokenCache, ok bool) {
-    url := "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
-    req := httplib.Get(url)
-    req.Param("corpid", corpid)     // 企业ID
-    req.Param("corpsecret", secret) // 应用的凭证密钥
-    req.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: false})
-    req.AddFilters(httpFilter)
-    resp, err := req.Response()
-    _ = resp
-    var token AccessTokenCache
-    if err != nil {
-        logs.Error(err)
-        return token, false
-    }
-    var atr AccessTokenResponse
-    err = req.ToJSON(&atr)
-    if err != nil {
-        logs.Error(err)
-        return token, false
-    }
-    token = AccessTokenCache{
-        AccessToken: atr.AccessToken,
-        ExpiresIn:   atr.ExpiresIn,
-        UpdateTime:  time.Now(),
-    }
-    return token, true
+	url := "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
+	req := httplib.Get(url)
+	req.Param("corpid", corpid)     // 企业ID
+	req.Param("corpsecret", secret) // 应用的凭证密钥
+	req.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: false})
+	req.AddFilters(httpFilter)
+	resp, err := req.Response()
+	_ = resp
+	var token AccessTokenCache
+	if err != nil {
+		logs.Error(err)
+		return token, false
+	}
+	var atr AccessTokenResponse
+	err = req.ToJSON(&atr)
+	if err != nil {
+		logs.Error(err)
+		return token, false
+	}
+	token = AccessTokenCache{
+		AccessToken: atr.AccessToken,
+		ExpiresIn:   atr.ExpiresIn,
+		UpdateTime:  time.Now(),
+	}
+	return token, true
 }
 
 // 获取访问凭据
 func GetAccessToken(is_contact bool) (access_token string, ok bool) {
-    var cache_token AccessTokenCache
-    cache_key := AccessTokenCacheKey
-    if is_contact {
-        cache_key = ContactAccessTokenCacheKey
-    }
-    err := cache.Get(cache_key, &cache_token)
-    if err == nil {
-        logs.Info("AccessToken从缓存读取成功")
-        // TODO: access_token有效期判断, 刷新
-        return cache_token.AccessToken, true
-    } else {
-        logs.Warning(err)
-        workweixinConfig := conf.GetWorkWeixinConfig()
-        logs.Debug("corp_id: ", workweixinConfig.CorpId)
-        logs.Debug("agent_id: ", workweixinConfig.AgentId)
-        logs.Debug("secret: ", workweixinConfig.Secret)
-        logs.Debug("contact_secret: ", workweixinConfig.ContactSecret)
-        secret := workweixinConfig.Secret
-        if is_contact {
-            secret = workweixinConfig.ContactSecret
-        }
-        new_token, ok := RequestAccessToken(workweixinConfig.CorpId, secret)
-        if ok {
-            logs.Debug(new_token)
-            if err = cache.Put(cache_key, new_token, time.Second*time.Duration(new_token.ExpiresIn)); err == nil {
-                logs.Info("AccessToken缓存写入成功")
-                return new_token.AccessToken, true
-            }
-            logs.Warning("AccessToken缓存写入失败")
-            return "", false
-        }
-        logs.Warning("AccessToken请求失败")
-        return "", false
-    }
+	var cache_token AccessTokenCache
+	cache_key := AccessTokenCacheKey
+	if is_contact {
+		cache_key = ContactAccessTokenCacheKey
+	}
+	err := cache.Get(cache_key, &cache_token)
+	if err == nil {
+		logs.Info("AccessToken从缓存读取成功")
+		// TODO: access_token有效期判断, 刷新
+		return cache_token.AccessToken, true
+	} else {
+		logs.Warning(err)
+		workweixinConfig := conf.GetWorkWeixinConfig()
+		logs.Debug("corp_id: ", workweixinConfig.CorpId)
+		logs.Debug("agent_id: ", workweixinConfig.AgentId)
+		logs.Debug("secret: ", workweixinConfig.Secret)
+		logs.Debug("contact_secret: ", workweixinConfig.ContactSecret)
+		secret := workweixinConfig.Secret
+		if is_contact {
+			secret = workweixinConfig.ContactSecret
+		}
+		new_token, ok := RequestAccessToken(workweixinConfig.CorpId, secret)
+		if ok {
+			logs.Debug(new_token)
+			if err = cache.Put(cache_key, new_token, time.Second*time.Duration(new_token.ExpiresIn)); err == nil {
+				logs.Info("AccessToken缓存写入成功")
+				return new_token.AccessToken, true
+			}
+			logs.Warning("AccessToken缓存写入失败")
+			return "", false
+		}
+		logs.Warning("AccessToken请求失败")
+		return "", false
+	}
 }
 
 // 获取用户id-请求
 func RequestUserId(access_token string, code string) (user_id string, ok bool) {
-    url := "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo"
-    req := httplib.Get(url)
-    req.Param("access_token", access_token) // 应用调用接口凭证
-    req.Param("code", code)                 // 通过成员授权获取到的code
-    req.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: false})
-    req.AddFilters(httpFilter)
-    resp, err := req.Response()
-    _ = resp
-    if err != nil {
-        logs.Error(err)
-        return "", false
-    }
-    var uir UserIdResponse
-    err = req.ToJSON(&uir)
-    if err != nil {
-        logs.Error(err)
-        return "", false
-    }
-    return uir.UserId, true
+	url := "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo"
+	req := httplib.Get(url)
+	req.Param("access_token", access_token) // 应用调用接口凭证
+	req.Param("code", code)                 // 通过成员授权获取到的code
+	req.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: false})
+	req.AddFilters(httpFilter)
+	resp, err := req.Response()
+	_ = resp
+	if err != nil {
+		logs.Error(err)
+		return "", false
+	}
+	var uir UserIdResponse
+	err = req.ToJSON(&uir)
+	if err != nil {
+		logs.Error(err)
+		return "", false
+	}
+	return uir.UserId, true
 }
 
-// 获取用户详细信息-请求
+/*
+获取用户详细信息-请求
+从2022年8月15日10点开始,“企业管理后台 - 管理工具 - 通讯录同步”的新增IP将不能再调用此接口
+url:https://developer.work.weixin.qq.com/document/path/96079
+*/
 func RequestUserInfo(contact_access_token string, userid string) (user_info WorkWeixinUserInfo, error_msg string, ok bool) {
-    url := "https://qyapi.weixin.qq.com/cgi-bin/user/get"
-    req := httplib.Get(url)
-    req.Param("access_token", contact_access_token) // 通讯录应用调用接口凭证
-    req.Param("userid", userid)                     // 成员UserID
-    req.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: false})
-    req.AddFilters(httpFilter)
-    resp_str, err := req.String()
-    _ = resp_str
-    var info WorkWeixinUserInfo
-    if err != nil {
-        logs.Error(err)
-        return info, "请求失败", false
-    } else {
-        logs.Debug(resp_str)
-    }
-    var uir UserInfoResponse
-    err = req.ToJSON(&uir)
-    if err != nil {
-        logs.Error(err)
-        return info, "请求数据结果错误", false
-    }
-    if uir.ErrCode != 0 {
-        return info, uir.ErrMsg, false
-    }
-    info = WorkWeixinUserInfo{
-        UserId:         uir.UserId,
-        Name:           uir.Name,
-        HideMobile:     uir.HideMobile,
-        Mobile:         uir.Mobile,
-        Department:     uir.Department,
-        Email:          uir.Email,
-        IsLeaderInDept: uir.IsLeaderInDept,
-        IsLeader:       uir.IsLeader,
-        Avatar:         uir.Avatar,
-        Alias:          uir.Alias,
-        Status:         uir.Status,
-        MainDepartment: uir.MainDepartment,
-    }
-    return info, "", true
+	url := "https://qyapi.weixin.qq.com/cgi-bin/user/get"
+	req := httplib.Get(url)
+	req.Param("access_token", contact_access_token) // 通讯录应用调用接口凭证
+	req.Param("userid", userid)                     // 成员UserID
+	req.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: false})
+	req.AddFilters(httpFilter)
+	resp_str, err := req.String()
+	_ = resp_str
+	var info WorkWeixinUserInfo
+	if err != nil {
+		logs.Error(err)
+		return info, "请求失败", false
+	} else {
+		logs.Debug(resp_str)
+	}
+	var uir UserInfoResponse
+	err = req.ToJSON(&uir)
+	if err != nil {
+		logs.Error(err)
+		return info, "请求数据结果错误", false
+	}
+	if uir.ErrCode != 0 {
+		return info, uir.ErrMsg, false
+	}
+	info = WorkWeixinUserInfo{
+		UserId:         uir.UserId,
+		Name:           uir.Name,
+		HideMobile:     uir.HideMobile,
+		Mobile:         uir.Mobile,
+		Department:     uir.Department,
+		Email:          uir.Email,
+		IsLeaderInDept: uir.IsLeaderInDept,
+		IsLeader:       uir.IsLeader,
+		Avatar:         uir.Avatar,
+		Alias:          uir.Alias,
+		Status:         uir.Status,
+		MainDepartment: uir.MainDepartment,
+	}
+	return info, "", true
+}
+
+/*
+获取成员ID列表
+*/
+func GetUserListId(contact_access_token string, userid string) (user_info WorkWeixinDeptUserInfo, error_msg string, ok bool) {
+	url := "https://qyapi.weixin.qq.com/cgi-bin/user/list_id"
+	req := httplib.Get(url)
+	req.Param("access_token", contact_access_token) // 通讯录应用调用接口凭证
+	req.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: false})
+	req.AddFilters(httpFilter)
+	respStr, err := req.String()
+	_ = respStr
+
+	var info WorkWeixinDeptUserInfo
+	if err != nil {
+		logs.Error(err)
+		return info, "请求失败", false
+	} else {
+		logs.Debug(respStr)
+	}
+
+	// 返回响应
+	var uir UserListIdResponse
+	//获取用户信息失败: 请求数据结果错误
+	err = req.ToJSON(&uir)
+	if err != nil {
+		logs.Error(err)
+		return info, "请求数据结果错误", false
+	}
+
+	if uir.ErrCode != 0 {
+		return info, uir.ErrMsg, false
+	}
+
+	// 判断userid 中是否还有当前用户id
+	for i := 0; i < len(uir.DeptUser); i++ {
+		if uir.DeptUser[i].UserId == userid {
+			info = WorkWeixinDeptUserInfo{
+				UserId: uir.DeptUser[i].UserId,
+			}
+		} else {
+			return info, uir.ErrMsg, false
+		}
+	}
+	return info, "", true
 }