- v2 token format changed

- DisableUser
- clear all tokens when disable user
- handle ding ding user leave event
This commit is contained in:
gazebo
2019-03-09 20:59:18 +08:00
parent 4b362baea9
commit 2e65154206
6 changed files with 117 additions and 21 deletions

View File

@@ -10,6 +10,7 @@ import (
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api" "git.rosy.net.cn/jx-callback/globals/api"
"github.com/dchest/captcha" "github.com/dchest/captcha"
@@ -24,6 +25,7 @@ const (
) )
const ( const (
TokenHeader = "TOKEN"
TokenVer = "V2" TokenVer = "V2"
TokenTypeSep = "." TokenTypeSep = "."
TokenUserEmpty = "NULL" TokenUserEmpty = "NULL"
@@ -37,8 +39,11 @@ const (
) )
const ( const (
DefTokenDuration = 7 * 24 * time.Hour // 7天 DefTokenDuration = 7 * 24 * time.Hour // 正式TOKEN7天有效期
TmpTokenDuration = 30 * time.Minute // 临时TOKEN30分钟有效期
MinCaptchaLen = 4 MinCaptchaLen = 4
MaxCaptchaWidth = 400
MaxCaptchaHeight = 400
) )
type IUser interface { type IUser interface {
@@ -113,21 +118,23 @@ func RegisterAuther(authType string, handler IAuther) {
func createAuthInfo(user IUser, authBindInfo *AuthBindEx) (authInfo *AuthInfo) { func createAuthInfo(user IUser, authBindInfo *AuthBindEx) (authInfo *AuthInfo) {
token, tokenType := createToken(user) token, tokenType := createToken(user)
expireDuration := DefTokenDuration
authInfo = &AuthInfo{ authInfo = &AuthInfo{
AuthBindInfo: authBindInfo, AuthBindInfo: authBindInfo,
LoginTime: time.Now(),
LoginTime: time.Now(), ExpiresIn: time.Now().Add(DefTokenDuration).Unix(),
ExpiresIn: time.Now().Add(DefTokenDuration).Unix(), Token: token,
Token: token, TokenType: tokenType,
TokenType: tokenType,
} }
if user != nil { if user != nil {
authInfo.UpdateByIUser(user) authInfo.UpdateByIUser(user)
globals.SugarLogger.Debugf("createAuthInfo id:%s, id2:%s, mobile:%s, authInfo:%s", authInfo.GetID(), authInfo.GetID2(), authInfo.GetMobile(), utils.Format4Output(authInfo, true)) globals.SugarLogger.Debugf("createAuthInfo id:%s, id2:%s, mobile:%s, authInfo:%s", authInfo.GetID(), authInfo.GetID2(), authInfo.GetMobile(), utils.Format4Output(authInfo, true))
} else { } else {
expireDuration = TmpTokenDuration
authInfo.ExpiresIn = time.Now().Add(expireDuration).Unix()
globals.SugarLogger.Debugf("createAuthInfo authInfo:%s", utils.Format4Output(authInfo, true)) globals.SugarLogger.Debugf("createAuthInfo authInfo:%s", utils.Format4Output(authInfo, true))
} }
SetUserInfo(token, authInfo, DefTokenDuration) SetUserInfo(token, authInfo, expireDuration)
return authInfo return authInfo
} }
@@ -135,6 +142,12 @@ func CreateCaptcha(width, height, captchaLen int) (captchaInfo *CaptchaInfo, err
if captchaLen < MinCaptchaLen { if captchaLen < MinCaptchaLen {
captchaLen = MinCaptchaLen captchaLen = MinCaptchaLen
} }
if width > MaxCaptchaWidth {
width = MaxCaptchaWidth
}
if height > MaxCaptchaHeight {
height = MaxCaptchaHeight
}
captchaInfo = &CaptchaInfo{ captchaInfo = &CaptchaInfo{
ID: captcha.NewLen(captchaLen), ID: captcha.NewLen(captchaLen),
} }
@@ -283,6 +296,7 @@ func Logout(authInfo *AuthInfo) (err error) {
} }
// token缓存相关 // token缓存相关
/////////////
func RemoveUserInfo(token string) { func RemoveUserInfo(token string) {
api.Cacher.Del(token) api.Cacher.Del(token)
@@ -300,17 +314,36 @@ func SetUserInfo(token string, authInfo *AuthInfo, duration time.Duration) {
api.Cacher.Set(token, authInfo, DefTokenDuration) api.Cacher.Set(token, authInfo, DefTokenDuration)
} }
func ClearUserToken(userID string) {
if keys, err := api.Cacher.Keys(strings.Join([]string{
TokenHeader,
TokenVer,
userID,
"*",
}, TokenTypeSep)); err == nil {
for _, key := range keys {
api.Cacher.Del(key)
}
}
}
/////////////
func createToken(user IUser) (token string, tokenType int) { func createToken(user IUser) (token string, tokenType int) {
userID := TokenUserEmpty userID := TokenUserEmpty
userName := TokenUserEmpty
tokenType = TokenTypeOnlyAuth tokenType = TokenTypeOnlyAuth
if user != nil { if user != nil {
userID = "[" + user.GetID2() + "]" userID = user.GetID()
userName = "[" + user.GetID2() + "]"
tokenType = TokenTypeNormal tokenType = TokenTypeNormal
} }
return strings.Join([]string{ return strings.Join([]string{
TokenHeader,
TokenVer, TokenVer,
time.Now().Format("20060102-150405"),
userID, userID,
time.Now().Format("20060102-150405"),
userName,
utils.GetUUID(), utils.GetUUID(),
}, TokenTypeSep), tokenType }, TokenTypeSep), tokenType
} }
@@ -319,7 +352,7 @@ func GetTokenType(token string) (tokenType int) {
tokenType = TokenTypeNone tokenType = TokenTypeNone
if token != "" { if token != "" {
tokenPartList := strings.Split(token, TokenTypeSep) tokenPartList := strings.Split(token, TokenTypeSep)
if (len(tokenPartList) == 1) || (len(tokenPartList) == 4 && tokenPartList[2] != TokenUserEmpty) { if (len(tokenPartList) == 1) || (len(tokenPartList) == 6 && tokenPartList[2] != TokenUserEmpty) {
tokenType = TokenTypeNormal tokenType = TokenTypeNormal
} else { } else {
tokenType = TokenTypeOnlyAuth tokenType = TokenTypeOnlyAuth
@@ -344,3 +377,14 @@ func GuessAuthTypeFromAuthID(authID string) (authType string) {
} }
return AuthTypeNone return AuthTypeNone
} }
func DisableUser(userID, operatorUserName string) (err error) {
if _, err = dao.UpdateEntityLogically(dao.GetDB(), &model.AuthBind{}, map[string]interface{}{
"Status": model.AuthBindStatusDisabled,
}, operatorUserName, map[string]interface{}{
"UserID": userID,
}); err == nil {
ClearUserToken(userID)
}
return err
}

View File

@@ -3,12 +3,15 @@ package cms
import ( import (
"errors" "errors"
"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/auth2" "git.rosy.net.cn/jx-callback/business/auth2"
"git.rosy.net.cn/jx-callback/business/auth2/authprovider/dingding"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
"git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/business/model/dao"
"git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api"
) )
var ( var (
@@ -111,3 +114,37 @@ func CreateUser(user *model.User) (err error) {
user.Status = model.UserStatusNormal user.Status = model.UserStatusNormal
return dao.CreateEntity(nil, user) return dao.CreateEntity(nil, user)
} }
func DisableUser(ctx *jxcontext.Context, userID string) (err error) {
userName := ctx.GetUserName()
if _, err = dao.UpdateEntityLogically(dao.GetDB(), &model.User{}, map[string]interface{}{
"Status": model.UserStatusDisabled,
}, userName, map[string]interface{}{
"UserID": userID,
}); err == nil {
auth2.DisableUser(userID, userName)
}
return err
}
func OnDingDingMsg(msg map[string]interface{}) (callbackResponse *dingdingapi.CallbackResponse) {
eventType := utils.Interface2String(msg[dingdingapi.KeyEventType])
if eventType == dingdingapi.CBTagUserLeaveOrg {
var (
authBind *model.AuthBind
err error
)
db := dao.GetDB()
for _, userID := range msg[dingdingapi.KeyUserID].([]interface{}) {
userIDStr := utils.Interface2String(userID)
globals.SugarLogger.Debugf("OnDingDingMsg dingding user:%s left company", userIDStr)
if authBind, err = dao.GetAuthBind(db, "", dingding.AuthTypeStaff, userIDStr); err == nil { // 直接找到了
globals.SugarLogger.Debugf("OnDingDingMsg dingding user:%s, userID:%s left company", userIDStr, authBind.UserID)
if err = DisableUser(jxcontext.AdminCtx, authBind.UserID); err != nil {
globals.SugarLogger.Errorf("OnDingDingMsg failed with error:%v", err)
}
}
}
}
return api.DingDingAPI.Err2CallbackResponse(nil)
}

View File

@@ -15,10 +15,11 @@ func GetAuthBind(db *DaoDB, userID, authType, authID string) (authBind *model.Au
sql := ` sql := `
SELECT * SELECT *
FROM auth_bind t1 FROM auth_bind t1
WHERE t1.deleted_at = ? WHERE t1.deleted_at = ? AND t1.status = ?
` `
sqlParams := []interface{}{ sqlParams := []interface{}{
utils.DefaultTimeValue, utils.DefaultTimeValue,
model.AuthBindStatusNormal,
} }
if userID != "" { if userID != "" {
sql += " AND t1.user_id = ?" sql += " AND t1.user_id = ?"
@@ -41,9 +42,10 @@ func GetAuthBindsByAuthID2(db *DaoDB, authID2 string, typeList []string) (authBi
sql := ` sql := `
SELECT * SELECT *
FROM auth_bind t1 FROM auth_bind t1
WHERE t1.deleted_at = ? AND t1.auth_id2 = ? AND t1.type IN (` + GenQuestionMarks(len(typeList)) + ")" WHERE t1.deleted_at = ? AND t1.status = ? AND t1.auth_id2 = ? AND t1.type IN (` + GenQuestionMarks(len(typeList)) + ")"
sqlParams := []interface{}{ sqlParams := []interface{}{
utils.DefaultTimeValue, utils.DefaultTimeValue,
model.AuthBindStatusNormal,
authID2, authID2,
typeList, typeList,
} }

View File

@@ -12,10 +12,11 @@ func GetUserByID(db *DaoDB, fieldName, fieldValue string) (user *model.User, err
sql := fmt.Sprintf(` sql := fmt.Sprintf(`
SELECT * SELECT *
FROM user t1 FROM user t1
WHERE t1.deleted_at = ? AND t1.%s = ? WHERE t1.deleted_at = ? AND t1.status = ? AND t1.%s = ?
`, fieldName) `, fieldName)
sqlParams := []interface{}{ sqlParams := []interface{}{
utils.DefaultTimeValue, utils.DefaultTimeValue,
model.UserStatusNormal,
fieldValue, fieldValue,
} }
globals.SugarLogger.Debugf("GetUserByID sql:%s, sqlParams:%s", sql, utils.Format4Output(sqlParams, false)) globals.SugarLogger.Debugf("GetUserByID sql:%s, sqlParams:%s", sql, utils.Format4Output(sqlParams, false))
@@ -27,8 +28,13 @@ func GetUserBindAuthInfo(db *DaoDB, userID string) (authList []*model.AuthBind,
sql := ` sql := `
SELECT * SELECT *
FROM auth_bind t1 FROM auth_bind t1
WHERE t1.deleted_at = ? AND t1.user_id = ? WHERE t1.deleted_at = ? AND t1.status = ? AND t1.user_id = ?
` `
err = GetRows(db, &authList, sql, utils.DefaultTimeValue, userID) sqlParams := []interface{}{
utils.DefaultTimeValue,
model.UserStatusNormal,
userID,
}
err = GetRows(db, &authList, sql, sqlParams...)
return authList, err return authList, err
} }

View File

@@ -4,6 +4,7 @@ import (
"net/http" "net/http"
"git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
"git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/jx-callback/globals"
"git.rosy.net.cn/jx-callback/globals/api" "git.rosy.net.cn/jx-callback/globals/api"
"github.com/astaxie/beego" "github.com/astaxie/beego"
@@ -24,7 +25,10 @@ func (c *DingDingController) Msg() {
obj, callbackResponse := api.DingDingAPI.GetCallbackMsg(dataMap, c.Ctx.Input.RequestBody) obj, callbackResponse := api.DingDingAPI.GetCallbackMsg(dataMap, c.Ctx.Input.RequestBody)
if callbackResponse == nil { if callbackResponse == nil {
globals.SugarLogger.Debugf("dingding msg, obj:%s", utils.Format4Output(obj, false)) globals.SugarLogger.Debugf("dingding msg, obj:%s", utils.Format4Output(obj, false))
callbackResponse = api.DingDingAPI.Err2CallbackResponse(nil) callbackResponse = cms.OnDingDingMsg(dataMap)
if callbackResponse == nil {
callbackResponse = api.DingDingAPI.Err2CallbackResponse(nil)
}
} }
c.Data["json"] = callbackResponse c.Data["json"] = callbackResponse
c.ServeJSON() c.ServeJSON()

13
main.go
View File

@@ -102,11 +102,14 @@ func main() {
orderman.LoadPendingOrders() orderman.LoadPendingOrders()
// 延时的原因是等回调准备好 // 延时的原因是等回调准备好
time.AfterFunc(2*time.Second, func() { if true { // beego.BConfig.RunMode == "prod" {
if err := api.DingDingAPI.RegisterCallback([]string{dingdingapi.CBTagUserAddOrg, dingdingapi.CBTagUserModifyOrg, dingdingapi.CBTagUserLeaveOrg}, beego.AppConfig.DefaultString("dingdingCallbackToken", ""), beego.AppConfig.DefaultString("dingdingCallbackAESKey", ""), beego.AppConfig.DefaultString("dingdingCallbackURL", "")); err != nil { time.AfterFunc(2*time.Second, func() {
globals.SugarLogger.Warnf("dingding RegisterCallback failed with error:%v", err) api.DingDingAPI.DeleteCallback()
} if err := api.DingDingAPI.RegisterCallback([]string{dingdingapi.CBTagUserAddOrg, dingdingapi.CBTagUserModifyOrg, dingdingapi.CBTagUserLeaveOrg}, beego.AppConfig.DefaultString("dingdingCallbackToken", ""), beego.AppConfig.DefaultString("dingdingCallbackAESKey", ""), beego.AppConfig.DefaultString("dingdingCallbackURL", "")); err != nil {
}) globals.SugarLogger.Warnf("dingding RegisterCallback failed with error:%v", err)
}
})
}
if beego.BConfig.RunMode != "prod" { if beego.BConfig.RunMode != "prod" {
beego.BConfig.WebConfig.DirectoryIndex = true beego.BConfig.WebConfig.DirectoryIndex = true