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

pull/814/head
code huan 2022-08-28 17:56:46 +08:00
parent 5a9d097344
commit b9510a7057
2 changed files with 256 additions and 187 deletions

View File

@ -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")

View File

@ -1,222 +1,287 @@
package workweixin
import (
"context"
"crypto/tls"
// "encoding/json"
"net/http"
"time"
"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"
"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
}
// 获取用户详细信息-请求
/*
-
202281510 - - 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
}