- first edition of auth2
This commit is contained in:
293
business/auth2/auth2.go
Normal file
293
business/auth2/auth2.go
Normal file
@@ -0,0 +1,293 @@
|
||||
package auth2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/globals"
|
||||
"git.rosy.net.cn/jx-callback/globals/api"
|
||||
)
|
||||
|
||||
const (
|
||||
UserIDEmpty = ""
|
||||
UserIDID = "userid"
|
||||
UserIDID2 = "userid2"
|
||||
UserIDMobile = "mobile"
|
||||
UserIDEmail = "email"
|
||||
)
|
||||
|
||||
const (
|
||||
TokenVer = "V2"
|
||||
TokenTypeSep = "."
|
||||
TokenUserEmpty = "NULL"
|
||||
)
|
||||
|
||||
const (
|
||||
AuthTypeNone = ""
|
||||
AuthTypePassword = "password"
|
||||
AuthTypeEmail = "email"
|
||||
AuthTypeMobile = "mobile"
|
||||
)
|
||||
|
||||
const (
|
||||
DefTokenDuration = 7 * 24 * time.Hour // 7天
|
||||
)
|
||||
|
||||
type IUser interface {
|
||||
GetID() string // 这个ID是不可变的,系统内部使用的唯一标识
|
||||
GetID2() string // 这个是可改的,唯一的,用户设置的用户名
|
||||
GetMobile() string
|
||||
GetEmail() string
|
||||
GetName() string
|
||||
}
|
||||
|
||||
type IUserProvider interface {
|
||||
GetUser(authID, authIDType string) (user IUser)
|
||||
}
|
||||
|
||||
type IAuther interface {
|
||||
SendVerifyCode(authID string) (err error)
|
||||
// 负责验证secret,并找到相应的用户返回(password,email,mobile类型的不负责用户查找)如果找不到用户UserID为空
|
||||
VerifySecret(authID, authSecret string) (authBind *model.AuthBind, err error)
|
||||
AddAuthBind(authBind *model.AuthBind, userName string) (err error)
|
||||
UnbindAuth(authInfo *AuthInfo, authType string) (err error)
|
||||
Logout(authInfo *AuthInfo) (err error)
|
||||
}
|
||||
|
||||
var (
|
||||
authers map[string]IAuther
|
||||
userProvider IUserProvider
|
||||
authTypeGuesserMap = map[string]*regexp.Regexp{
|
||||
AuthTypeEmail: regexp.MustCompile(`^[A-Za-z0-9_\-\.]+\@[A-Za-z0-9_\-]+(\.[A-Za-z]+){1,5}$`),
|
||||
AuthTypeMobile: regexp.MustCompile(`^1[34578]\d{9}$`),
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInternalErrror = errors.New("内部错误")
|
||||
ErrTokenIsInvalid = errors.New("Token非法")
|
||||
ErrUserAlreadyExist = errors.New("用户已经存在")
|
||||
ErrUserNotExist = errors.New("用户不存在")
|
||||
ErrIllegalAuthType = errors.New("非法的登录类型")
|
||||
ErrIllegalAuthTypeAlreadyExist = errors.New("要登录类型已经存在")
|
||||
)
|
||||
|
||||
func init() {
|
||||
authers = make(map[string]IAuther)
|
||||
}
|
||||
|
||||
func Init(userProvider2 IUserProvider) {
|
||||
userProvider = userProvider2
|
||||
}
|
||||
|
||||
func RegisterAuther(authType string, handler IAuther) {
|
||||
authers[authType] = handler
|
||||
}
|
||||
|
||||
func CreateauthInfo(user IUser, authBindInfo *model.AuthBind, userData interface{}) (authInfo *AuthInfo) {
|
||||
token, tokenType := createToken(user)
|
||||
authInfo = &AuthInfo{
|
||||
IUser: user,
|
||||
AuthBindInfo: authBindInfo,
|
||||
|
||||
LoginTime: time.Now(),
|
||||
ExpiresIn: time.Now().Add(DefTokenDuration).Unix(),
|
||||
Token: token,
|
||||
TokenType: tokenType,
|
||||
UserData: userData,
|
||||
}
|
||||
if user != nil {
|
||||
globals.SugarLogger.Debugf("CreateauthInfo id:%s, id2:%s, authBindInfo:%s, authInfo:%s", authInfo.GetID(), authInfo.GetID2(), authInfo.GetMobile(), utils.Format4Output(authBindInfo, true), utils.Format4Output(authInfo, true))
|
||||
} else {
|
||||
globals.SugarLogger.Debugf("CreateauthInfo authBindInfo:%s, authInfo:%s", utils.Format4Output(authBindInfo, true), utils.Format4Output(authInfo, true))
|
||||
}
|
||||
SetUserInfo(token, authInfo, DefTokenDuration)
|
||||
return authInfo
|
||||
}
|
||||
|
||||
// 账号密码时:authIDType可能是:UserIDID,UserIDID2,UserIDMobile,UserIDEmail,authSecret是密码的sha1
|
||||
// 邮箱时(如果允许):authIDType是:UserIDEmail,authSecret是验证码的sha1
|
||||
// 手机时(如果允许):authIDType是:UserIDMobile,authSecret是验证码的sha1
|
||||
// 公众号登录:authIDTypeD是UserIDEmpty,authSecret是code(这个函数是被微信的回调调用,不是直接被客户端调用)
|
||||
// 微信登录:authIDType是UserIDEmpty,authSecret是code(这个函数是被微信的回调调用,不是直接被客户端调用)
|
||||
// 小程序登录:authIDType是UserIDEmpty,authSecret是jsCode
|
||||
func Login(authType, authID, authIDType, authSecret string) (authInfo *AuthInfo, err error) {
|
||||
if handler := authers[authType]; handler != nil {
|
||||
var authBind *model.AuthBind
|
||||
var user IUser
|
||||
realauthID := authID
|
||||
if authIDType == AuthTypePassword {
|
||||
if user = userProvider.GetUser(authID, authIDType); user == nil {
|
||||
return nil, ErrUserNotExist
|
||||
}
|
||||
realauthID = user.GetID()
|
||||
}
|
||||
if authBind, err = handler.VerifySecret(realauthID, authSecret); err == nil {
|
||||
if authBind == nil { // mobile, email会返回nil(表示不会新建AuthBind实体)
|
||||
user = userProvider.GetUser(authID, authIDType)
|
||||
authBind = &model.AuthBind{
|
||||
Type: authType,
|
||||
AuthID: authID,
|
||||
Status: model.AuthBindStatusNormal,
|
||||
}
|
||||
} else {
|
||||
// 返回authBind中UserID为空表示只是认证,但本地没有记录,这种情况会返回临时TOKEN
|
||||
if authBind.UserID != "" {
|
||||
user = userProvider.GetUser(authBind.UserID, UserIDID)
|
||||
}
|
||||
}
|
||||
authInfo = CreateauthInfo(user, authBind, nil)
|
||||
}
|
||||
} else {
|
||||
err = ErrIllegalAuthType
|
||||
}
|
||||
return authInfo, err
|
||||
}
|
||||
|
||||
// 通过临时TOKEN绑定新创建的用户
|
||||
func BindUser(inauthInfo *AuthInfo, user IUser) (outauthInfo *AuthInfo, err error) {
|
||||
if inauthInfo == nil || user == nil {
|
||||
return nil, ErrInternalErrror
|
||||
}
|
||||
if inauthInfo.IUser != nil {
|
||||
return nil, ErrUserAlreadyExist
|
||||
}
|
||||
if handler := authers[inauthInfo.AuthBindInfo.Type]; handler != nil {
|
||||
inauthInfo.AuthBindInfo.UserID = user.GetID()
|
||||
if err = handler.AddAuthBind(inauthInfo.AuthBindInfo, user.GetName()); err == nil {
|
||||
RemoveUserInfo(inauthInfo.Token)
|
||||
outauthInfo = CreateauthInfo(user, inauthInfo.AuthBindInfo, inauthInfo.UserData)
|
||||
}
|
||||
} else {
|
||||
err = ErrIllegalAuthType
|
||||
}
|
||||
return outauthInfo, err
|
||||
}
|
||||
|
||||
// 添加新绑定
|
||||
func AddAuthBind(authInfo *AuthInfo, newAuthInfo *AuthInfo) (err error) {
|
||||
if authInfo == nil || newAuthInfo == nil {
|
||||
return ErrInternalErrror
|
||||
}
|
||||
if newAuthInfo.IUser != nil {
|
||||
return ErrIllegalAuthTypeAlreadyExist
|
||||
}
|
||||
RemoveUserInfo(newAuthInfo.Token)
|
||||
newAuthInfo.AuthBindInfo.UserID = authInfo.GetID()
|
||||
err = authers[newAuthInfo.AuthBindInfo.Type].AddAuthBind(newAuthInfo.AuthBindInfo, authInfo.GetName())
|
||||
return err
|
||||
}
|
||||
|
||||
// func AddAuthBind(authInfo *AuthInfo, authType, authID, authIDType, authSecret string) (err error) {
|
||||
// if authInfo == nil {
|
||||
// return ErrInternalErrror
|
||||
// }
|
||||
// // 密码绑定直接绑定
|
||||
// if authType == AuthTypePassword {
|
||||
// err = authers[AuthTypePassword].AddAuthBind(&model.AuthBind{
|
||||
// UserID: authInfo.GetID(),
|
||||
// Type: AuthTypePassword,
|
||||
// AuthID: authInfo.GetID(),
|
||||
// Status: model.AuthBindStatusNormal,
|
||||
// }, authInfo.GetName())
|
||||
// } else {
|
||||
// tmpauthInfo, err := Login(authType, authID, authIDType, authSecret)
|
||||
// if err == nil {
|
||||
// RemoveUserInfo(tmpauthInfo.Token)
|
||||
// tmpauthInfo.AuthBindInfo.UserID = authInfo.GetID()
|
||||
// err = authers[authType].AddAuthBind(tmpauthInfo.AuthBindInfo, authInfo.GetName())
|
||||
// }
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
|
||||
func UnbindAuth(authInfo *AuthInfo, authType string) (err error) {
|
||||
if handler := authers[authType]; handler != nil {
|
||||
err = handler.UnbindAuth(authInfo, authType)
|
||||
} else {
|
||||
err = ErrIllegalAuthType
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func Logout(authInfo *AuthInfo) (err error) {
|
||||
globals.SugarLogger.Debugf("Logout authInfo:%s", utils.Format4Output(authInfo, true))
|
||||
if handler := authers[authInfo.AuthBindInfo.Type]; handler != nil {
|
||||
err = handler.Logout(authInfo)
|
||||
}
|
||||
RemoveUserInfo(authInfo.Token)
|
||||
return err
|
||||
}
|
||||
|
||||
func SendVerifyCode(authID string) (err error) {
|
||||
authType := GuessAuthTypeFromAuthID(authID)
|
||||
if handler := authers[authType]; handler == nil {
|
||||
return ErrIllegalAuthType
|
||||
} else {
|
||||
return handler.SendVerifyCode(authID)
|
||||
}
|
||||
}
|
||||
|
||||
// token缓存相关
|
||||
|
||||
func RemoveUserInfo(token string) {
|
||||
api.Cacher.Del(token)
|
||||
}
|
||||
|
||||
func GetUserInfo(token string) (authInfo *AuthInfo, err error) {
|
||||
authInfo = new(AuthInfo)
|
||||
if err = api.Cacher.GetAs(token, authInfo); err == nil {
|
||||
return authInfo, nil
|
||||
}
|
||||
return nil, ErrTokenIsInvalid
|
||||
}
|
||||
|
||||
func SetUserInfo(token string, authInfo *AuthInfo, duration time.Duration) {
|
||||
api.Cacher.Set(token, authInfo, DefTokenDuration)
|
||||
}
|
||||
|
||||
func createToken(user IUser) (token string, tokenType int) {
|
||||
userID := TokenUserEmpty
|
||||
tokenType = TokenTypeOnlyAuth
|
||||
if user != nil {
|
||||
userID = "[" + user.GetID2() + "]"
|
||||
tokenType = TokenTypeNormal
|
||||
}
|
||||
return strings.Join([]string{
|
||||
TokenVer,
|
||||
time.Now().Format("20060102-150405"),
|
||||
userID,
|
||||
userID,
|
||||
}, TokenTypeSep), tokenType
|
||||
}
|
||||
|
||||
func GetTokenType(token string) (tokenType int) {
|
||||
tokenType = TokenTypeNone
|
||||
if token != "" {
|
||||
tokenPartList := strings.Split(token, TokenTypeSep)
|
||||
if (len(tokenPartList) == 1) || (len(tokenPartList) == 4 && tokenPartList[2] != TokenUserEmpty) {
|
||||
tokenType = TokenTypeNormal
|
||||
} else {
|
||||
tokenType = TokenTypeOnlyAuth
|
||||
}
|
||||
}
|
||||
return tokenType
|
||||
}
|
||||
|
||||
func IsV2Token(token string) bool {
|
||||
tokenPartList := strings.Split(token, TokenTypeSep)
|
||||
return tokenPartList[0] == TokenVer
|
||||
}
|
||||
|
||||
func GuessAuthTypeFromAuthID(authID string) (authType string) {
|
||||
for k, v := range authTypeGuesserMap {
|
||||
if v.FindStringSubmatch(authID) != nil {
|
||||
return k
|
||||
}
|
||||
}
|
||||
return AuthTypeNone
|
||||
}
|
||||
Reference in New Issue
Block a user