jflyfox 4 жил өмнө
parent
commit
118f2de8a1

+ 1 - 1
example/sample1/config/config.toml

@@ -1,5 +1,5 @@
 [server]
-    Address        = "127.0.0.1:80"
+    Address        = "127.0.0.1:8081"
     ServerAgent    = "gtoken-demo"
     LogPath        = "./example/sample1/logs"
 

+ 6 - 6
example/sample1/main.go

@@ -28,20 +28,20 @@ var gfToken *gtoken.GfToken
 统一路由注册
 */
 func initRouter(s *ghttp.Server) {
-	s.Group("/", func(g *ghttp.RouterGroup) {
-		g.Middleware(CORS)
+	s.Group("/", func(group *ghttp.RouterGroup) {
+		group.Middleware(CORS)
 
 		// 调试路由
-		g.ALL("/hello", func(r *ghttp.Request) {
+		group.ALL("/hello", func(r *ghttp.Request) {
 			r.Response.WriteJson(gtoken.Succ("hello"))
 		})
-		g.ALL("/system/user", func(r *ghttp.Request) {
+		group.ALL("/system/user", func(r *ghttp.Request) {
 			r.Response.WriteJson(gtoken.Succ("system user"))
 		})
-		g.ALL("/user/info", func(r *ghttp.Request) {
+		group.ALL("/user/info", func(r *ghttp.Request) {
 			r.Response.WriteJson(gtoken.Succ("user info"))
 		})
-		g.ALL("/system/user/info", func(r *ghttp.Request) {
+		group.ALL("/system/user/info", func(r *ghttp.Request) {
 			r.Response.WriteJson(gtoken.Succ("system user info"))
 		})
 	})

+ 1 - 1
example/sample1/test/api_test.go

@@ -9,7 +9,7 @@ import (
 )
 
 const (
-	TestURL string = "http://127.0.0.1:80"
+	TestURL string = "http://127.0.0.1:8081"
 )
 
 var (

+ 15 - 0
example/sample2/config/config.toml

@@ -0,0 +1,15 @@
+[server]
+    Address        = "127.0.0.1:8082"
+    ServerAgent    = "gtoken-demo"
+    LogPath        = "./example/sample1/logs"
+
+[gtoken]
+    # 缓存模式 1 gcache 2 gredis
+    cache-mode = 1
+    # 是否支持多端登录
+    multi-login = true
+
+# Redis数据库配置
+[redis]
+  default = "127.0.0.1:16379,0,soccer"
+  cache   = "127.0.0.1:16379,1,soccer?idleTimeout=600"

+ 87 - 0
example/sample2/main.go

@@ -0,0 +1,87 @@
+package main
+
+import (
+	"github.com/goflyfox/gtoken/gtoken"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/os/glog"
+)
+
+var TestServerName string
+
+//var TestServerName string = "gtoken"
+
+func main() {
+	glog.Info("########service start...")
+
+	g.Cfg().SetPath("example/sample2")
+	s := g.Server(TestServerName)
+	initRouter(s)
+
+	glog.Info("########service finish.")
+	s.Run()
+}
+
+var gfToken *gtoken.GfToken
+
+/*
+统一路由注册
+*/
+func initRouter(s *ghttp.Server) {
+	// 不认证接口
+	s.Group("/", func(group *ghttp.RouterGroup) {
+		group.Middleware(CORS)
+
+		// 调试路由
+		group.ALL("/hello", func(r *ghttp.Request) {
+			r.Response.WriteJson(gtoken.Succ("hello"))
+		})
+	})
+
+	// 认证接口
+	loginFunc := Login
+	// 启动gtoken
+	gfToken := &gtoken.GfToken{
+		ServerName: TestServerName,
+		//Timeout:         10 * 1000,
+		CacheMode:        g.Config().GetInt8("gtoken.cache-mode"),
+		LoginPath:        "/login",
+		LoginBeforeFunc:  loginFunc,
+		LogoutPath:       "/user/logout",
+		AuthExcludePaths: g.SliceStr{"/user/info", "/system/user/info"}, // 不拦截路径 /user/info,/system/user/info,/system/user,
+		MultiLogin:       g.Config().GetBool("gtoken.multi-login"),
+	}
+	s.Group("/", func(group *ghttp.RouterGroup) {
+		group.Middleware(CORS)
+		gfToken.Middleware(group)
+
+		group.ALL("/system/user", func(r *ghttp.Request) {
+			r.Response.WriteJson(gtoken.Succ("system user"))
+		})
+		group.ALL("/user/info", func(r *ghttp.Request) {
+			r.Response.WriteJson(gtoken.Succ("user info"))
+		})
+		group.ALL("/system/user/info", func(r *ghttp.Request) {
+			r.Response.WriteJson(gtoken.Succ("system user info"))
+		})
+	})
+
+}
+
+func Login(r *ghttp.Request) (string, interface{}) {
+	username := r.GetString("username")
+	passwd := r.GetString("passwd")
+
+	if username == "" || passwd == "" {
+		r.Response.WriteJson(gtoken.Fail("账号或密码错误."))
+		r.ExitAll()
+	}
+
+	return username, "1"
+}
+
+// 跨域
+func CORS(r *ghttp.Request) {
+	r.Response.CORSDefault()
+	r.Middleware.Next()
+}

+ 3 - 0
example/sample2/test/api.http

@@ -0,0 +1,3 @@
+GET http://localhost/system/user
+
+###

+ 303 - 0
example/sample2/test/api_test.go

@@ -0,0 +1,303 @@
+package test
+
+import (
+	"encoding/json"
+	"github.com/goflyfox/gtoken/gtoken"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/net/ghttp"
+	"testing"
+)
+
+const (
+	TestURL string = "http://127.0.0.1:8082"
+)
+
+var (
+	Token    = g.MapStrStr{}
+	Username = "flyfox"
+)
+
+func TestHello(t *testing.T) {
+	t.Log("visit hello and no auth")
+	if r, e := ghttp.Post(TestURL+"/hello", "username="+Username); e != nil {
+		t.Error("error:", e)
+	} else {
+		defer r.Close()
+
+		content := string(r.ReadAll())
+		t.Log(content)
+
+		var respData gtoken.Resp
+		err := json.Unmarshal([]byte(content), &respData)
+		if err != nil {
+			t.Error("error:", err)
+		}
+		if !respData.Success() {
+			t.Error("error:", respData.Json())
+		}
+	}
+}
+
+func TestSystemUser(t *testing.T) {
+	// 未登录
+	t.Log("1. not login and visit user")
+	if r, e := ghttp.Post(TestURL+"/system/user", "username="+Username); e != nil {
+		t.Error("error:", e)
+	} else {
+		defer r.Close()
+
+		content := string(r.ReadAll())
+		t.Log(content)
+
+		var respData gtoken.Resp
+		err := json.Unmarshal([]byte(content), &respData)
+		if err != nil {
+			t.Error("error:", err)
+		}
+		if respData.Success() {
+			t.Error("error:", respData.Json())
+		}
+	}
+
+	// 登录,访问用户信息
+	t.Log("2. execute login and visit user")
+	data := Post(t, "/system/user", "username="+Username)
+	if data.Success() {
+		t.Log(data.Json())
+	} else {
+		t.Error("error:", data.Json())
+	}
+
+	// 登出
+	t.Log("3. execute logout")
+	data = Post(t, "/user/logout", "username="+Username)
+	if data.Success() {
+		t.Log(data.Json())
+	} else {
+		t.Error("error:", data.Json())
+	}
+
+	// 登出访问用户信息
+	t.Log("4. visit user")
+	data = Post(t, "/system/user", "username="+Username)
+	if data.Success() {
+		t.Error("error:", data.Json())
+	} else {
+		t.Log(data.Json())
+	}
+}
+
+func TestUserLoginFail(t *testing.T) {
+	// 登录失败
+	t.Log("1. login fail ")
+	if r, e := ghttp.Post(TestURL+"/login", "username=&passwd="); e != nil {
+		t.Error("error:", e)
+	} else {
+		defer r.Close()
+
+		content := string(r.ReadAll())
+
+		var respData gtoken.Resp
+		err := json.Unmarshal([]byte(content), &respData)
+		if err != nil {
+			t.Error("error:", err)
+		}
+
+		if respData.Success() {
+			t.Error("error:", "login fail:"+respData.Json())
+		}
+	}
+
+}
+
+func TestExclude(t *testing.T) {
+	// 未登录可以访问
+	t.Log("1. exclude user info")
+	if r, e := ghttp.Post(TestURL+"/system/user/info", "username="+Username); e != nil {
+		t.Error("error:", e)
+	} else {
+		defer r.Close()
+
+		content := string(r.ReadAll())
+		t.Log(content)
+
+		var respData gtoken.Resp
+		err := json.Unmarshal([]byte(content), &respData)
+		if err != nil {
+			t.Error("error:", err)
+		}
+		if !respData.Success() {
+			t.Error("error:", respData.Json())
+		}
+	}
+
+	if r, e := ghttp.Post(TestURL+"/user/info", "username="+Username); e != nil {
+		t.Error("error:", e)
+	} else {
+		defer r.Close()
+
+		content := string(r.ReadAll())
+		t.Log(content)
+
+		var respData gtoken.Resp
+		err := json.Unmarshal([]byte(content), &respData)
+		if err != nil {
+			t.Error("error:", err)
+		}
+		if !respData.Success() {
+			t.Error("error:", respData.Json())
+		}
+	}
+
+}
+
+//func TestRefresh(t *testing.T) {
+//	// 登录,访问用户信息
+//	t.Log("1. execute login and visit user")
+//	data := Post(t, "/system/user", "username="+Username)
+//	if data.Success() {
+//		t.Log(data.Json())
+//	} else {
+//		t.Error("error:", data.Json())
+//	}
+//
+//	for i := 1; i < 9; i++ {
+//		time.Sleep(2 * time.Second)
+//		// 登录,访问用户信息
+//		t.Log("1. execute login and visit user")
+//		data = Post(t, "/system/user", "username="+Username)
+//		if data.Success() {
+//			t.Log(data.Json())
+//		} else {
+//			t.Error("error:", data.Json())
+//		}
+//	}
+//
+//}
+
+func TestLogin(t *testing.T) {
+	Username = "testLogin"
+	t.Log(" login first ")
+	token1 := getToken(t)
+	t.Log("token:" + token1)
+	t.Log(" login second and same token ")
+	token2 := getToken(t)
+	t.Log("token:" + token2)
+	if token1 != token2 {
+		t.Error("error:", "token not same ")
+	}
+	Username = "flyfox"
+}
+
+func TestLogout(t *testing.T) {
+	Username = "testLogout"
+	t.Log(" logout test ")
+	data := Post(t, "/user/logout", "username="+Username)
+	if data.Success() {
+		t.Log(data.Json())
+	} else {
+		t.Error("error:", data.Json())
+	}
+	Username = "flyfox"
+}
+
+func Post(t *testing.T, urlPath string, data ...interface{}) gtoken.Resp {
+	client := ghttp.NewClient()
+	client.SetHeader("Authorization", "Bearer "+getToken(t))
+	content := client.RequestContent("POST", TestURL+urlPath, data...)
+	var respData gtoken.Resp
+	err := json.Unmarshal([]byte(content), &respData)
+	if err != nil {
+		t.Error("error:", err)
+	}
+	return respData
+}
+
+func getToken(t *testing.T) string {
+	if Token[Username] != "" {
+		return Token[Username]
+	}
+
+	if r, e := ghttp.Post(TestURL+"/login", "username="+Username+"&passwd=123456"); e != nil {
+		t.Error("error:", e)
+	} else {
+		defer r.Close()
+
+		content := string(r.ReadAll())
+
+		var respData gtoken.Resp
+		err := json.Unmarshal([]byte(content), &respData)
+		if err != nil {
+			t.Error("error:", err)
+		}
+
+		if !respData.Success() {
+			t.Error("error:", "resp fail:"+respData.Json())
+		}
+
+		Token[Username] = respData.GetString("token")
+	}
+	return Token[Username]
+}
+
+func TestMultiLogin(t *testing.T) {
+	Username = "testLogin"
+	t.Log(" TestMultiLogin start... ")
+	var token1, token2 string
+	if r, e := ghttp.Post(TestURL+"/login", "username="+Username+"&passwd=123456"); e != nil {
+		t.Error("error:", e)
+	} else {
+		defer r.Close()
+
+		content := string(r.ReadAll())
+		t.Log("token1 content:" + content)
+
+		var respData gtoken.Resp
+		err := json.Unmarshal([]byte(content), &respData)
+		if err != nil {
+			t.Error("error:", err)
+		}
+
+		if !respData.Success() {
+			t.Error("error:", "resp fail:"+respData.Json())
+		}
+
+		token1 = respData.GetString("token")
+	}
+	t.Log("token1:" + token1)
+
+	if r, e := ghttp.Post(TestURL+"/login", "username="+Username+"&passwd=123456"); e != nil {
+		t.Error("error:", e)
+	} else {
+		defer r.Close()
+
+		content := string(r.ReadAll())
+		t.Log("token2 content:" + content)
+
+		var respData gtoken.Resp
+		err := json.Unmarshal([]byte(content), &respData)
+		if err != nil {
+			t.Error("error:", err)
+		}
+
+		if !respData.Success() {
+			t.Error("error:", "resp fail:"+respData.Json())
+		}
+
+		token2 = respData.GetString("token")
+	}
+
+	t.Log("token2:" + token2)
+
+	if g.Config().GetBool("gtoken.multi-login") {
+		if token1 != token2 {
+			t.Error("error:", "token not same ")
+		}
+	} else {
+		if token1 == token2 {
+			t.Error("error:", "token same ")
+		}
+	}
+
+	Username = "flyfox"
+}

+ 102 - 0
example/sample2/test/cache_test.go

@@ -0,0 +1,102 @@
+package test
+
+import (
+	"github.com/goflyfox/gtoken/gtoken"
+	"github.com/gogf/gf/encoding/gjson"
+	"github.com/gogf/gf/frame/g"
+	"github.com/gogf/gf/os/gcache"
+	"github.com/gogf/gf/util/gconv"
+	"testing"
+	"time"
+)
+
+func TestGCache(t *testing.T) {
+	t.Log("gcache test ")
+	userKey := "123123"
+	gcache.Set(userKey, "1", 10000)
+
+	value, err := gcache.Get(userKey)
+	if err != nil {
+		t.Error("cache set error," + err.Error())
+	}
+	if value.(string) == userKey {
+		t.Error("cache get error")
+	}
+
+	gcache.Remove(userKey)
+
+	value, err = gcache.Get(userKey)
+	if err != nil {
+		t.Error("cache set error," + err.Error())
+	}
+	if value != nil {
+		t.Error("cache remove error")
+	}
+
+}
+
+func TestRedisCache(t *testing.T) {
+	if g.Config().GetInt8("gtoken.cache-mode") != gtoken.CacheModeRedis {
+		t.Log("redis cache not test ")
+		return
+	}
+
+	t.Log("redis cache test ")
+	userKey := "test:a"
+	_, err := g.Redis().Do("SETEX", userKey, 10000, "1")
+	if err != nil {
+		t.Error("cache set error," + err.Error())
+	}
+
+	time.Sleep(1 * time.Second)
+	ttl, err2 := g.Redis().Do("TTL", userKey)
+	if err2 != nil {
+		t.Error("cache ttl error," + err.Error())
+	}
+	t.Log("ttl:" + gconv.String(ttl))
+	if gconv.Int(ttl) >= 10000 || gconv.Int(ttl) < 9000 {
+		t.Error("cache ttl error, ttl:" + gconv.String(ttl))
+	}
+
+	data, err3 := g.Redis().Do("GET", userKey)
+	if err3 != nil {
+		t.Error("cache get error," + err.Error())
+	}
+	t.Log("data:" + gconv.String(data))
+	if gconv.String(data) != "1" {
+		t.Error("cache get error, data:" + gconv.String(data))
+	}
+
+	g.Redis().Do("DEL", userKey)
+	data, err4 := g.Redis().Do("GET", userKey)
+	if err4 != nil {
+		t.Error("cache del get error," + err.Error())
+	}
+	if gconv.String(data) != "" {
+		t.Error("cache del error, data:" + gconv.String(data))
+	}
+}
+
+func TestJson(t *testing.T) {
+	t.Log("json test ")
+	cacheValue := g.Map{
+		"userKey": "123",
+		"uuid":    "abc",
+		"data":    "",
+	}
+
+	cacheValueJson, err1 := gjson.Encode(cacheValue)
+	if err1 != nil {
+		t.Error("cache json encode error:" + err1.Error())
+	}
+
+	var userCache g.Map
+	err2 := gjson.DecodeTo(cacheValueJson, &userCache)
+	if err2 != nil {
+		t.Error("cache get json error:" + err2.Error())
+	}
+
+	if gconv.Map(userCache)["userKey"] != "123" {
+		t.Error("cache get json  data error:" + gconv.String(userCache))
+	}
+}

+ 26 - 0
example/sample2/test/time_test.go

@@ -0,0 +1,26 @@
+package test
+
+import (
+	"github.com/gogf/gf/os/gtime"
+	"github.com/gogf/gf/util/gconv"
+	"testing"
+	"time"
+)
+
+func TestTime(t *testing.T) {
+	t.Log("time test ")
+	time1 := gtime.Now().TimestampMilli()
+	t.Log("time time1: ", time1)
+	time.Sleep(time.Second * 2)
+	time2 := gtime.Now().TimestampMilli()
+	if time2-time1 < 1 {
+		t.Error("time error:" + gconv.String(time2-time1))
+	}
+}
+
+func TestTime2(t *testing.T) {
+	t.Log("time test2")
+	time1 := 10 * 1000
+	t.Log("###", gconv.Duration(time1)*time.Millisecond)
+	t.Log("###", (gconv.Duration(time1) * time.Millisecond).Milliseconds())
+}

+ 25 - 4
gtoken/gtoken.go

@@ -18,6 +18,10 @@ import (
 const (
 	CacheModeCache = 1
 	CacheModeRedis = 2
+
+	MiddlewareTypeGroup  = 1
+	MiddlewareTypeBind   = 2
+	MiddlewareTypeGlobal = 3
 )
 
 // GfToken gtoken结构体
@@ -40,8 +44,10 @@ type GfToken struct {
 	AuthFailMsg string
 	// 是否支持多端登录,默认false
 	MultiLogin bool
-	// 是否是全局认证
+	// 是否是全局认证,兼容历史版本,已废弃
 	GlobalMiddleware bool
+	// 中间件类型 1 GroupMiddleware 2 BindMiddleware  3 GlobalMiddleware
+	MiddlewareType uint
 
 	// 登录路径
 	LoginPath string
@@ -96,6 +102,15 @@ func (m *GfToken) Init() bool {
 		m.AuthFailMsg = "请求错误或登录超时"
 	}
 
+	// 设置中间件模式,未设置说明历史版本,通过GlobalMiddleware兼容
+	if m.MiddlewareType == 0 {
+		if m.GlobalMiddleware {
+			m.MiddlewareType = MiddlewareTypeGlobal
+		} else {
+			m.MiddlewareType = MiddlewareTypeBind
+		}
+	}
+
 	if m.LoginAfterFunc == nil {
 		m.LoginAfterFunc = func(r *ghttp.Request, respData Resp) {
 			if !respData.Success() {
@@ -185,7 +200,7 @@ func (m *GfToken) Start() bool {
 	}
 
 	// 是否是全局拦截
-	if m.GlobalMiddleware {
+	if m.MiddlewareType == MiddlewareTypeGlobal {
 		s.BindMiddlewareDefault(m.AuthMiddleware)
 	} else {
 		for _, authPath := range m.AuthPaths {
@@ -302,9 +317,15 @@ func (m *GfToken) AuthPath(urlPath string) bool {
 	if strings.HasSuffix(urlPath, "/") {
 		urlPath = gstr.SubStr(urlPath, 0, len(urlPath)-1)
 	}
+	// 分组拦截,登录接口不拦截
+	if m.MiddlewareType == MiddlewareTypeGroup {
+		if gstr.HasSuffix(urlPath, m.LoginPath) {
+			return false
+		}
+	}
 
 	// 全局处理,认证路径拦截处理
-	if m.GlobalMiddleware {
+	if m.MiddlewareType == MiddlewareTypeGlobal {
 		var authFlag bool
 		for _, authPath := range m.AuthPaths {
 			tmpPath := authPath
@@ -528,7 +549,7 @@ func (m *GfToken) String() string {
 		"EncryptKey":       string(m.EncryptKey),
 		"AuthFailMsg":      m.AuthFailMsg,
 		"MultiLogin":       m.MultiLogin,
-		"GlobalMiddleware": m.GlobalMiddleware,
+		"MiddlewareType":   m.MiddlewareType,
 		"LoginPath":        m.LoginPath,
 		"LogoutPath":       m.LogoutPath,
 		"AuthPaths":        gconv.String(m.AuthPaths),

+ 38 - 0
gtoken/gtoken_group.go

@@ -0,0 +1,38 @@
+package gtoken
+
+import (
+	"github.com/gogf/gf/net/ghttp"
+	"github.com/gogf/gf/os/glog"
+)
+
+// 绑定group
+func (m *GfToken) Middleware(group *ghttp.RouterGroup) bool {
+	if !m.Init() {
+		return false
+	}
+	// 设置为Group模式
+	m.MiddlewareType = MiddlewareTypeGroup
+	glog.Info("[GToken][params:" + m.String() + "]start... ")
+
+	// 缓存模式
+	if m.CacheMode > CacheModeRedis {
+		glog.Error("[GToken]CacheMode set error")
+		return false
+	}
+	// 登录
+	if m.LoginPath == "" || m.LoginBeforeFunc == nil {
+		glog.Error("[GToken]LoginPath or LoginBeforeFunc not set")
+		return false
+	}
+	// 登出
+	if m.LogoutPath == "" {
+		glog.Error("[GToken]LogoutPath or LogoutFunc not set")
+		return false
+	}
+
+	group.Middleware(m.AuthMiddleware)
+	group.ALL(m.LoginPath, m.Login)
+	group.ALL(m.LogoutPath, m.Logout)
+
+	return true
+}