|
|
@@ -0,0 +1,521 @@
|
|
|
+package gtoken
|
|
|
+
|
|
|
+import (
|
|
|
+ "strings"
|
|
|
+
|
|
|
+ "dashoo.cn/micro_libary/utils"
|
|
|
+ "github.com/gogf/gf/crypto/gaes"
|
|
|
+ "github.com/gogf/gf/crypto/gmd5"
|
|
|
+ "github.com/gogf/gf/encoding/gbase64"
|
|
|
+ "github.com/gogf/gf/frame/g"
|
|
|
+ "github.com/gogf/gf/net/ghttp"
|
|
|
+ "github.com/gogf/gf/os/glog"
|
|
|
+ "github.com/gogf/gf/os/gtime"
|
|
|
+ "github.com/gogf/gf/text/gstr"
|
|
|
+ "github.com/gogf/gf/util/gconv"
|
|
|
+ "github.com/gogf/gf/util/grand"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ CacheModeCache = 1
|
|
|
+ CacheModeRedis = 2
|
|
|
+)
|
|
|
+
|
|
|
+// GfToken gtoken结构体
|
|
|
+type GfToken struct {
|
|
|
+ // 缓存模式 1 gcache 2 gredis 默认1
|
|
|
+ CacheMode int8
|
|
|
+ // 缓存key
|
|
|
+ CacheKey string
|
|
|
+ // 超时时间 默认10天
|
|
|
+ Timeout int
|
|
|
+ // 缓存刷新时间 默认为超时时间的一半
|
|
|
+ MaxRefresh int
|
|
|
+ // Token分隔符
|
|
|
+ TokenDelimiter string
|
|
|
+ // Token加密key
|
|
|
+ EncryptKey []byte
|
|
|
+ // 认证失败中文提示
|
|
|
+ AuthFailMsg string
|
|
|
+ // 是否支持多端登录,默认false
|
|
|
+ MultiLogin bool
|
|
|
+
|
|
|
+ // 登录路径
|
|
|
+ LoginPath string
|
|
|
+ // 登录验证方法 return userKey 用户标识 如果userKey为空,结束执行
|
|
|
+ LoginBeforeFunc func(r *ghttp.Request) (string, interface{})
|
|
|
+ // 登录返回方法
|
|
|
+ LoginAfterFunc func(r *ghttp.Request, respData Resp)
|
|
|
+ // 登出地址
|
|
|
+ LogoutPath string
|
|
|
+ // 登出验证方法 return true 继续执行,否则结束执行
|
|
|
+ LogoutBeforeFunc func(r *ghttp.Request) bool
|
|
|
+ // 登出返回方法
|
|
|
+ LogoutAfterFunc func(r *ghttp.Request, respData Resp)
|
|
|
+
|
|
|
+ // 拦截地址
|
|
|
+ AuthPaths g.SliceStr
|
|
|
+ // 认证验证方法 return true 继续执行,否则结束执行
|
|
|
+ AuthBeforeFunc func(r *ghttp.Request) bool
|
|
|
+ // 认证返回方法
|
|
|
+ AuthAfterFunc func(r *ghttp.Request, respData Resp)
|
|
|
+}
|
|
|
+
|
|
|
+// Init 初始化
|
|
|
+func (m *GfToken) Init() bool {
|
|
|
+ if m.CacheMode == 0 {
|
|
|
+ m.CacheMode = CacheModeCache
|
|
|
+ }
|
|
|
+
|
|
|
+ if m.CacheKey == "" {
|
|
|
+ m.CacheKey = "GToken:"
|
|
|
+ }
|
|
|
+
|
|
|
+ if m.Timeout == 0 {
|
|
|
+ m.Timeout = 10 * 24 * 60 * 60 * 1000
|
|
|
+ }
|
|
|
+
|
|
|
+ if m.MaxRefresh == 0 {
|
|
|
+ m.MaxRefresh = m.Timeout / 2
|
|
|
+ }
|
|
|
+
|
|
|
+ if m.TokenDelimiter == "" {
|
|
|
+ m.TokenDelimiter = "_"
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(m.EncryptKey) == 0 {
|
|
|
+ m.EncryptKey = []byte("12345678912345678912345678912345")
|
|
|
+ }
|
|
|
+
|
|
|
+ if m.AuthFailMsg == "" {
|
|
|
+ m.AuthFailMsg = "请求错误或登录超时"
|
|
|
+ }
|
|
|
+
|
|
|
+ //if m.LoginAfterFunc == nil {
|
|
|
+ // m.LoginAfterFunc = func(r *ghttp.Request, respData Resp) {
|
|
|
+ // if !respData.Success() {
|
|
|
+ // r.Response.WriteJson(respData)
|
|
|
+ // } else {
|
|
|
+ // r.Response.WriteJson(Succ(g.Map{
|
|
|
+ // "token": respData.GetString("token"),
|
|
|
+ // }))
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ //}
|
|
|
+ //
|
|
|
+ //if m.LogoutBeforeFunc == nil {
|
|
|
+ // m.LogoutBeforeFunc = func(r *ghttp.Request) bool {
|
|
|
+ // return true
|
|
|
+ // }
|
|
|
+ //}
|
|
|
+ //
|
|
|
+ //if m.LogoutAfterFunc == nil {
|
|
|
+ // m.LogoutAfterFunc = func(r *ghttp.Request, respData Resp) {
|
|
|
+ // if respData.Success() {
|
|
|
+ // r.Response.WriteJson(Succ("Logout success"))
|
|
|
+ // } else {
|
|
|
+ // r.Response.WriteJson(respData)
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ //}
|
|
|
+ //
|
|
|
+ //if m.AuthBeforeFunc == nil {
|
|
|
+ // m.AuthBeforeFunc = func(r *ghttp.Request) bool {
|
|
|
+ // // 静态页面不拦截
|
|
|
+ // if r.IsFileRequest() {
|
|
|
+ // return false
|
|
|
+ // }
|
|
|
+ //
|
|
|
+ // return true
|
|
|
+ // }
|
|
|
+ //}
|
|
|
+ //if m.AuthAfterFunc == nil {
|
|
|
+ // m.AuthAfterFunc = func(r *ghttp.Request, respData Resp) {
|
|
|
+ // if respData.Success() {
|
|
|
+ // r.Middleware.Next()
|
|
|
+ // } else {
|
|
|
+ // var params map[string]interface{}
|
|
|
+ // if r.Method == "GET" {
|
|
|
+ // params = r.GetMap()
|
|
|
+ // } else if r.Method == "POST" {
|
|
|
+ // params = r.GetMap()
|
|
|
+ // } else {
|
|
|
+ // r.Response.Writeln("Request Method is ERROR! ")
|
|
|
+ // return
|
|
|
+ // }
|
|
|
+ //
|
|
|
+ // no := gconv.String(gtime.TimestampMilli())
|
|
|
+ //
|
|
|
+ // glog.Info(fmt.Sprintf("[AUTH_%s][url:%s][params:%s][data:%s]",
|
|
|
+ // no, r.URL.Path, params, respData.Json()))
|
|
|
+ // respData.Msg = m.AuthFailMsg
|
|
|
+ // r.Response.WriteJson(respData)
|
|
|
+ // r.ExitAll()
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ //}
|
|
|
+
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+// Start 启动
|
|
|
+func (m *GfToken) Start() bool {
|
|
|
+ if !m.Init() {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ glog.Info("[GToken][params:" + m.String() + "]start... ")
|
|
|
+
|
|
|
+ //s := g.Server()
|
|
|
+
|
|
|
+ // 缓存模式
|
|
|
+ if m.CacheMode > CacheModeRedis {
|
|
|
+ glog.Error("[GToken]CacheMode set error")
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ // 认证拦截器
|
|
|
+ //if m.AuthPaths == nil {
|
|
|
+ // glog.Error("[GToken]HookPathList not set")
|
|
|
+ // return false
|
|
|
+ //}
|
|
|
+ //for _, authPath := range m.AuthPaths {
|
|
|
+ // if strings.HasSuffix(authPath, "/*") {
|
|
|
+ // s.BindMiddleware(authPath, m.AuthMiddleware)
|
|
|
+ // } else {
|
|
|
+ // s.BindMiddleware(authPath+"/*", m.AuthMiddleware)
|
|
|
+ // }
|
|
|
+ //}
|
|
|
+
|
|
|
+ // 登录
|
|
|
+ //if m.LoginPath == "" || m.LoginBeforeFunc == nil {
|
|
|
+ // glog.Error("[GToken]LoginPath or LoginBeforeFunc not set")
|
|
|
+ // return false
|
|
|
+ //}
|
|
|
+ //s.BindHandler(m.LoginPath, m.Login)
|
|
|
+
|
|
|
+ // 登出
|
|
|
+ //if m.LogoutPath == "" {
|
|
|
+ // glog.Error("[GToken]LogoutPath or LogoutFunc not set")
|
|
|
+ // return false
|
|
|
+ //}
|
|
|
+ //s.BindHandler(m.LogoutPath, m.Logout)
|
|
|
+
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+// Start 结束
|
|
|
+func (m *GfToken) Stop() bool {
|
|
|
+ glog.Info("[GToken]stop. ")
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+// GetTokenData 通过token获取对象
|
|
|
+func (m *GfToken) GetTokenData(r *ghttp.Request) Resp {
|
|
|
+ respData := m.getRequestToken(r)
|
|
|
+ if respData.Success() {
|
|
|
+ // 验证token
|
|
|
+ respData = m.validToken(respData.DataString())
|
|
|
+ }
|
|
|
+
|
|
|
+ return respData
|
|
|
+}
|
|
|
+
|
|
|
+// Login 登录
|
|
|
+func (m *GfToken) Login(r *ghttp.Request) {
|
|
|
+ userKey, data := m.LoginBeforeFunc(r)
|
|
|
+ if userKey == "" {
|
|
|
+ glog.Error("[GToken]Login userKey is empty")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if m.MultiLogin {
|
|
|
+ // 支持多端重复登录,返回相同token
|
|
|
+ userCacheResp := m.getToken(userKey, "")
|
|
|
+ if userCacheResp.Success() {
|
|
|
+ respToken := m.EncryptToken(userKey, "", userCacheResp.GetString("uuid"))
|
|
|
+ m.LoginAfterFunc(r, respToken)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成token
|
|
|
+ respToken := m.genToken(userKey, "", "", data)
|
|
|
+ m.LoginAfterFunc(r, respToken)
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// Logout 登出
|
|
|
+func (m *GfToken) Logout(r *ghttp.Request) {
|
|
|
+ if m.LogoutBeforeFunc(r) {
|
|
|
+ // 获取请求token
|
|
|
+ respData := m.getRequestToken(r)
|
|
|
+ if respData.Success() {
|
|
|
+ // 删除token
|
|
|
+ m.removeToken(respData.DataString())
|
|
|
+ }
|
|
|
+
|
|
|
+ m.LogoutAfterFunc(r, respData)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// AuthMiddleware 认证拦截
|
|
|
+func (m *GfToken) AuthMiddleware(r *ghttp.Request) {
|
|
|
+ // 不需要认证,直接下一步
|
|
|
+ if !m.AuthBeforeFunc(r) {
|
|
|
+ r.Middleware.Next()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取请求token
|
|
|
+ tokenResp := m.getRequestToken(r)
|
|
|
+ if tokenResp.Success() {
|
|
|
+ // 验证token
|
|
|
+ tokenResp = m.validToken(tokenResp.DataString())
|
|
|
+ }
|
|
|
+
|
|
|
+ m.AuthAfterFunc(r, tokenResp)
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// getRequestToken 返回请求Token
|
|
|
+func (m *GfToken) getRequestToken(r *ghttp.Request) Resp {
|
|
|
+ authHeader := r.Header.Get("Authorization")
|
|
|
+ if authHeader != "" {
|
|
|
+ parts := strings.SplitN(authHeader, " ", 2)
|
|
|
+ if !(len(parts) == 2 && parts[0] == "Bearer") {
|
|
|
+ glog.Warning("[GToken]authHeader:" + authHeader + " get token key fail")
|
|
|
+ return Unauthorized("get token key fail", "")
|
|
|
+ } else if parts[1] == "" {
|
|
|
+ glog.Warning("[GToken]authHeader:" + authHeader + " get token fail")
|
|
|
+ return Unauthorized("get token fail", "")
|
|
|
+ }
|
|
|
+
|
|
|
+ return Succ(parts[1])
|
|
|
+ }
|
|
|
+
|
|
|
+ authHeader = r.GetString("token")
|
|
|
+ if authHeader == "" {
|
|
|
+ return Unauthorized("query token fail", "")
|
|
|
+ }
|
|
|
+ return Succ(authHeader)
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+// genToken 生成Token
|
|
|
+func (m *GfToken) genToken(tenant, userKey, uuid string, data interface{}) Resp {
|
|
|
+ token := m.EncryptToken(tenant, userKey, uuid)
|
|
|
+ if !token.Success() {
|
|
|
+ return token
|
|
|
+ }
|
|
|
+
|
|
|
+ cacheKey := m.CacheKey + tenant + userKey
|
|
|
+ userCache := g.Map{
|
|
|
+ "tenant": tenant,
|
|
|
+ "userKey": userKey,
|
|
|
+ "uuid": token.GetString("uuid"),
|
|
|
+ "data": data,
|
|
|
+ "createTime": gtime.Now().Millisecond(),
|
|
|
+ "refreshTime": gtime.Now().Millisecond() + m.MaxRefresh,
|
|
|
+ }
|
|
|
+
|
|
|
+ cacheResp := m.setCache(cacheKey, userCache)
|
|
|
+ if !cacheResp.Success() {
|
|
|
+ return cacheResp
|
|
|
+ }
|
|
|
+
|
|
|
+ return token
|
|
|
+}
|
|
|
+
|
|
|
+// validToken 验证Token
|
|
|
+func (m *GfToken) validToken(token string) Resp {
|
|
|
+ if token == "" {
|
|
|
+ return Unauthorized("valid token empty", "")
|
|
|
+ }
|
|
|
+
|
|
|
+ decryptToken := m.DecryptToken(token)
|
|
|
+ if !decryptToken.Success() {
|
|
|
+ return decryptToken
|
|
|
+ }
|
|
|
+
|
|
|
+ userKey := decryptToken.GetString("userKey")
|
|
|
+ uuid := decryptToken.GetString("uuid")
|
|
|
+
|
|
|
+ userCacheResp := m.getToken(userKey, "")
|
|
|
+ if !userCacheResp.Success() {
|
|
|
+ return userCacheResp
|
|
|
+ }
|
|
|
+
|
|
|
+ if uuid != userCacheResp.GetString("uuid") {
|
|
|
+ glog.Error("[GToken]user auth error, decryptToken:" + decryptToken.Json() + " cacheValue:" + gconv.String(userCacheResp.Data))
|
|
|
+ return Unauthorized("user auth error", "")
|
|
|
+ }
|
|
|
+
|
|
|
+ return userCacheResp
|
|
|
+}
|
|
|
+
|
|
|
+// getToken 通过userKey获取Token
|
|
|
+func (m *GfToken) getToken(tenant, userKey string) Resp {
|
|
|
+ cacheKey := m.CacheKey + tenant + userKey
|
|
|
+
|
|
|
+ userCacheResp := m.getCache(cacheKey)
|
|
|
+ if !userCacheResp.Success() {
|
|
|
+ return userCacheResp
|
|
|
+ }
|
|
|
+ userCache := gconv.Map(userCacheResp.Data)
|
|
|
+
|
|
|
+ nowTime := gtime.Now().Millisecond()
|
|
|
+ refreshTime := userCache["refreshTime"]
|
|
|
+
|
|
|
+ // 需要进行缓存超时时间刷新
|
|
|
+ if gconv.Int64(refreshTime) == 0 || nowTime > gconv.Int(refreshTime) {
|
|
|
+ userCache["createTime"] = gtime.Now().Millisecond()
|
|
|
+ userCache["refreshTime"] = gtime.Now().Millisecond() + m.MaxRefresh
|
|
|
+ glog.Debug("[GToken]refreshToken:" + gconv.String(userCache))
|
|
|
+ return m.setCache(cacheKey, userCache)
|
|
|
+ }
|
|
|
+
|
|
|
+ return Succ(userCache)
|
|
|
+}
|
|
|
+
|
|
|
+// removeToken 删除Token
|
|
|
+func (m *GfToken) removeToken(token string) Resp {
|
|
|
+ decryptToken := m.DecryptToken(token)
|
|
|
+ if !decryptToken.Success() {
|
|
|
+ return decryptToken
|
|
|
+ }
|
|
|
+
|
|
|
+ cacheKey := m.CacheKey + decryptToken.GetString("userKey")
|
|
|
+ return m.removeCache(cacheKey)
|
|
|
+}
|
|
|
+
|
|
|
+// EncryptToken token加密方法
|
|
|
+func (m *GfToken) EncryptToken(tenant, userKey string, uuid string) Resp {
|
|
|
+ if userKey == "" {
|
|
|
+ return Fail("encrypt userKey empty")
|
|
|
+ }
|
|
|
+ if tenant == "" {
|
|
|
+ return Fail("encrypt tenant empty")
|
|
|
+ }
|
|
|
+ if uuid == "" {
|
|
|
+ // 重新生成uuid
|
|
|
+ newUuid, err := gmd5.Encrypt(grand.Str(utils.LetterDigits, 10))
|
|
|
+ if err != nil {
|
|
|
+ glog.Error("[GToken]uuid error", err)
|
|
|
+ return Error("uuid error")
|
|
|
+ }
|
|
|
+ uuid = newUuid
|
|
|
+ }
|
|
|
+
|
|
|
+ tokenStr := tenant + m.TokenDelimiter + userKey + m.TokenDelimiter + uuid
|
|
|
+ token, err := gaes.Encrypt([]byte(tokenStr), m.EncryptKey)
|
|
|
+ if err != nil {
|
|
|
+ glog.Error("[GToken]encrypt error", err)
|
|
|
+ return Error("encrypt error")
|
|
|
+ }
|
|
|
+ return Succ(g.Map{
|
|
|
+ "userKey": userKey,
|
|
|
+ "tenant": tenant,
|
|
|
+ "uuid": uuid,
|
|
|
+ "token": string(gbase64.Encode(token)),
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// DecryptToken token解密方法
|
|
|
+func (m *GfToken) DecryptToken(token string) Resp {
|
|
|
+ if token == "" {
|
|
|
+ return Fail("decrypt token empty")
|
|
|
+ }
|
|
|
+
|
|
|
+ token64, err := gbase64.Decode([]byte(token))
|
|
|
+ if err != nil {
|
|
|
+ glog.Error("[GToken]decode error", err)
|
|
|
+ return Error("decode error")
|
|
|
+ }
|
|
|
+
|
|
|
+ decryptToken, err2 := gaes.Decrypt([]byte(token64), m.EncryptKey)
|
|
|
+ if err2 != nil {
|
|
|
+ glog.Error("[GToken]decrypt error", err2)
|
|
|
+ return Error("decrypt error")
|
|
|
+ }
|
|
|
+ tokenArray := gstr.Split(string(decryptToken), m.TokenDelimiter)
|
|
|
+ if len(tokenArray) < 2 {
|
|
|
+ glog.Error("[GToken]token len error")
|
|
|
+ return Error("token len error")
|
|
|
+ }
|
|
|
+
|
|
|
+ return Succ(g.Map{
|
|
|
+ "tenant": tokenArray[0],
|
|
|
+ "userKey": tokenArray[1],
|
|
|
+ "uuid": tokenArray[2],
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// String token解密方法
|
|
|
+func (m *GfToken) String() string {
|
|
|
+ return gconv.String(g.Map{
|
|
|
+ // 缓存模式 1 gcache 2 gredis 默认1
|
|
|
+ "CacheMode": m.CacheMode,
|
|
|
+ "CacheKey": m.CacheKey,
|
|
|
+ "Timeout": m.Timeout,
|
|
|
+ "TokenDelimiter": m.TokenDelimiter,
|
|
|
+ "EncryptKey": string(m.EncryptKey),
|
|
|
+ "LoginPath": m.LoginPath,
|
|
|
+ "LogoutPath": m.LogoutPath,
|
|
|
+ "AuthPaths": gconv.String(m.AuthPaths),
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 微服务改造新增函数
|
|
|
+// 登录验证后获取或生成Token
|
|
|
+func (m *GfToken) GetOrGenToken(tenant, userKey, uuid string, data interface{}) Resp {
|
|
|
+ if m.MultiLogin {
|
|
|
+ // 支持多端重复登录,返回相同token
|
|
|
+ userCacheResp := m.getToken(tenant, userKey)
|
|
|
+ if userCacheResp.Success() {
|
|
|
+ respToken := m.EncryptToken(tenant, userKey, userCacheResp.GetString("uuid"))
|
|
|
+ return respToken
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 生成token
|
|
|
+ respToken := m.genToken(tenant, userKey, uuid, data)
|
|
|
+ return respToken
|
|
|
+}
|
|
|
+
|
|
|
+// removeToken 删除Token
|
|
|
+func (m *GfToken) RemoveToken(token string) Resp {
|
|
|
+ decryptToken := m.DecryptToken(token)
|
|
|
+ if !decryptToken.Success() {
|
|
|
+ return decryptToken
|
|
|
+ }
|
|
|
+
|
|
|
+ cacheKey := m.CacheKey + decryptToken.GetString("tenant") + decryptToken.GetString("userKey")
|
|
|
+ return m.removeCache(cacheKey)
|
|
|
+}
|
|
|
+
|
|
|
+// validToken 验证Token
|
|
|
+func (m *GfToken) ValidToken(token string) Resp {
|
|
|
+ if token == "" {
|
|
|
+ return Unauthorized("valid token empty", "")
|
|
|
+ }
|
|
|
+
|
|
|
+ decryptToken := m.DecryptToken(token)
|
|
|
+ if !decryptToken.Success() {
|
|
|
+ return decryptToken
|
|
|
+ }
|
|
|
+
|
|
|
+ userKey := decryptToken.GetString("userKey")
|
|
|
+ uuid := decryptToken.GetString("uuid")
|
|
|
+ tenant := decryptToken.GetString("tenant")
|
|
|
+
|
|
|
+ userCacheResp := m.getToken(tenant, userKey)
|
|
|
+ if !userCacheResp.Success() {
|
|
|
+ return userCacheResp
|
|
|
+ }
|
|
|
+
|
|
|
+ if uuid != userCacheResp.GetString("uuid") {
|
|
|
+ glog.Error("[GToken]user auth error, decryptToken:" + decryptToken.Json() + " cacheValue:" + gconv.String(userCacheResp.Data))
|
|
|
+ return Unauthorized("user auth error", "")
|
|
|
+ }
|
|
|
+
|
|
|
+ return userCacheResp
|
|
|
+}
|