소스 검색

add Exclude Path

zhangbiao 5 년 전
부모
커밋
290ff161ec
6개의 변경된 파일223개의 추가작업 그리고 21개의 파일을 삭제
  1. 3 0
      ChangeLog.md
  2. 13 0
      README.md
  3. 9 2
      example/main.go
  4. 41 0
      example/test/api_test.go
  5. 61 19
      gtoken/gtoken.go
  6. 96 0
      gtoken/gtoken_test.go

+ 3 - 0
ChangeLog.md

@@ -1,5 +1,8 @@
 Change Log 更新说明
 ------------------------------
+## 2020-04-1 v1.3.16
+1. 支持路径排除配置
+
 ## 2020-03-31 v1.3.15
 1. gf升级为V1.12.1
 

+ 13 - 0
README.md

@@ -71,6 +71,19 @@ func Login(r *ghttp.Request) (string, interface{}) {
 }
 ```
 
+#### 路径拦截规则
+```go
+		AuthPaths:        g.SliceStr{"/user", "/system"},             // 这里是按照前缀拦截,拦截/user /user/list /user/add ...
+		AuthExcludePaths: g.SliceStr{"/user/info", "/system/user/*"}, // 不拦截路径  /user/info,/system/user/info,/system/user,
+        GlobalMiddleware: true,                           // 开启全局拦截,默认关闭
+```
+
+1. `GlobalMiddleware:true`全局拦截的是通过GF的`BindMiddleware`方法创建拦截`/*`
+2. `GlobalMiddleware:false`路径拦截的是通过GF的`BindMiddleware`方法创建拦截`/user*和/system/*`
+3. 按照中间件优先级路径拦截优先级很高;如果先实现部分中间件在认证前处理需要切换成全局拦截器,严格按照注册顺序即可;
+4. 程序先处理认证路径,如果满足;再排除不拦截路径;
+5. 如果只想用排除路径功能,将拦截路径设置为`/*`即可;
+
 #### 逻辑测试
 
 可运行api_test.go进行测试并查看结果;验证逻辑说明:

+ 9 - 2
example/main.go

@@ -63,6 +63,12 @@ func bindRouter() {
 	s.BindHandler("/system/user", func(r *ghttp.Request) {
 		r.Response.WriteJson(gtoken.Succ("system user"))
 	})
+	s.BindHandler("/user/info", func(r *ghttp.Request) {
+		r.Response.WriteJson(gtoken.Succ("user info"))
+	})
+	s.BindHandler("/system/user/info", func(r *ghttp.Request) {
+		r.Response.WriteJson(gtoken.Succ("system user info"))
+	})
 
 	loginFunc := Login
 	// 启动gtoken
@@ -72,8 +78,9 @@ func bindRouter() {
 		LoginPath:        "/login",
 		LoginBeforeFunc:  loginFunc,
 		LogoutPath:       "/user/logout",
-		AuthPaths:        g.SliceStr{"/user", "/system"}, // 这里是按照前缀拦截,拦截/user /user/list /user/add ...
-		GlobalMiddleware: true,                           // 开启全局拦截
+		AuthPaths:        g.SliceStr{"/user", "/system"},                // 这里是按照前缀拦截,拦截/user /user/list /user/add ...
+		AuthExcludePaths: g.SliceStr{"/user/info", "/system/user/info"}, // 不拦截路径 /user/info,/system/user/info,/system/user,
+		GlobalMiddleware: true,                                          // 开启全局拦截
 		MultiLogin:       g.Config().GetBool("gtoken.multi-login"),
 	}
 	gfToken.Start()

+ 41 - 0
example/test/api_test.go

@@ -87,6 +87,47 @@ func TestSystemUser(t *testing.T) {
 	}
 }
 
+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")

+ 61 - 19
gtoken/gtoken.go

@@ -56,6 +56,8 @@ type GfToken struct {
 
 	// 拦截地址
 	AuthPaths g.SliceStr
+	// 拦截排除地址
+	AuthExcludePaths g.SliceStr
 	// 认证验证方法 return true 继续执行,否则结束执行
 	AuthBeforeFunc func(r *ghttp.Request) bool
 	// 认证返回方法
@@ -182,7 +184,7 @@ func (m *GfToken) Start() bool {
 
 	// 是否是全局拦截
 	if m.GlobalMiddleware {
-		s.BindMiddleware("/*", m.AuthMiddleware)
+		s.BindMiddlewareDefault(m.AuthMiddleware)
 	} else {
 		for _, authPath := range m.AuthPaths {
 			tmpPath := authPath
@@ -267,24 +269,11 @@ func (m *GfToken) Logout(r *ghttp.Request) {
 
 // AuthMiddleware 认证拦截
 func (m *GfToken) AuthMiddleware(r *ghttp.Request) {
-	// 全局处理,认证路径拦截处理
-	if m.GlobalMiddleware {
-		urlPath := r.URL.Path
-		var nextFlag bool
-		for _, authPath := range m.AuthPaths {
-			tmpPath := authPath
-			if strings.HasSuffix(authPath, "/*") {
-				tmpPath = gstr.SubStr(tmpPath, 0, len(tmpPath)-2)
-			}
-			if gstr.HasPrefix(urlPath, authPath) {
-				nextFlag = true
-			}
-		}
-
-		if !nextFlag {
-			r.Middleware.Next()
-			return
-		}
+	urlPath := r.URL.Path
+	if !m.AuthPath(urlPath) {
+		// 如果不需要认证,继续
+		r.Middleware.Next()
+		return
 	}
 
 	// 不需要认证,直接下一步
@@ -304,6 +293,59 @@ func (m *GfToken) AuthMiddleware(r *ghttp.Request) {
 
 }
 
+// 判断路径是否需要进行认证拦截
+// return true 需要认证
+func (m *GfToken) AuthPath(urlPath string) bool {
+	// 去除后斜杠
+	if strings.HasSuffix(urlPath, "/") {
+		urlPath = gstr.SubStr(urlPath, 0, len(urlPath)-1)
+	}
+
+	// 全局处理,认证路径拦截处理
+	if m.GlobalMiddleware {
+		var authFlag bool
+		for _, authPath := range m.AuthPaths {
+			tmpPath := authPath
+			if strings.HasSuffix(tmpPath, "/*") {
+				tmpPath = gstr.SubStr(tmpPath, 0, len(tmpPath)-2)
+			}
+			if gstr.HasPrefix(urlPath, tmpPath) {
+				authFlag = true
+				break
+			}
+		}
+
+		if !authFlag {
+			// 拦截路径不匹配
+			return false
+		}
+	}
+
+	// 排除路径处理,到这里nextFlag为true
+	for _, excludePath := range m.AuthExcludePaths {
+		tmpPath := excludePath
+		// 前缀匹配
+		if strings.HasSuffix(tmpPath, "/*") {
+			tmpPath = gstr.SubStr(tmpPath, 0, len(tmpPath)-2)
+			if gstr.HasPrefix(urlPath, tmpPath) {
+				// 前缀匹配不拦截
+				return false
+			}
+		} else {
+			// 全路径匹配
+			if strings.HasSuffix(tmpPath, "/") {
+				tmpPath = gstr.SubStr(tmpPath, 0, len(tmpPath)-1)
+			}
+			if urlPath == tmpPath {
+				// 全路径匹配不拦截
+				return false
+			}
+		}
+	}
+
+	return true
+}
+
 // getRequestToken 返回请求Token
 func (m *GfToken) getRequestToken(r *ghttp.Request) Resp {
 	authHeader := r.Header.Get("Authorization")

+ 96 - 0
gtoken/gtoken_test.go

@@ -2,9 +2,105 @@ package gtoken_test
 
 import (
 	"github.com/goflyfox/gtoken/gtoken"
+	"github.com/gogf/gf/frame/g"
 	"testing"
 )
 
+func TestAuthPathGlobal(t *testing.T) {
+	t.Log("auth path test ")
+	// 启动gtoken
+	gfToken := &gtoken.GfToken{
+		//Timeout:         10 * 1000,
+		AuthPaths:        g.SliceStr{"/user", "/system"},             // 这里是按照前缀拦截,拦截/user /user/list /user/add ...
+		AuthExcludePaths: g.SliceStr{"/user/info", "/system/user/*"}, // 不拦截路径  /user/info,/system/user/info,/system/user,
+		GlobalMiddleware: true,                                       // 开启全局拦截
+	}
+
+	authPath(gfToken, t)
+	flag := gfToken.AuthPath("/test")
+	if flag {
+		t.Error("/test auth path error")
+	}
+
+}
+
+func TestAuthPath(t *testing.T) {
+	t.Log("auth path test ")
+	// 启动gtoken
+	gfToken := &gtoken.GfToken{
+		//Timeout:         10 * 1000,
+		AuthPaths:        g.SliceStr{"/user", "/system"},             // 这里是按照前缀拦截,拦截/user /user/list /user/add ...
+		AuthExcludePaths: g.SliceStr{"/user/info", "/system/user/*"}, // 不拦截路径  /user/info,/system/user/info,/system/user,
+		GlobalMiddleware: false,                                      // 关闭全局拦截
+	}
+
+	authPath(gfToken, t)
+}
+
+func TestAuthPathExclude(t *testing.T) {
+	t.Log("auth path test ")
+	// 启动gtoken
+	gfToken := &gtoken.GfToken{
+		//Timeout:         10 * 1000,
+		AuthPaths:        g.SliceStr{"/*"},                           // 这里是按照前缀拦截,拦截/user /user/list /user/add ...
+		AuthExcludePaths: g.SliceStr{"/user/info", "/system/user/*"}, // 不拦截路径  /user/info,/system/user/info,/system/user,
+		GlobalMiddleware: true,                                       // 开启全局拦截
+	}
+
+	authFlag := gfToken.AuthPath
+	if !authFlag("/test") {
+		t.Error("/test auth path error")
+	}
+	if !authFlag("//system/dept") {
+		t.Error("/system/dept auth path error")
+	}
+
+	if authFlag("/user/info") {
+		t.Error("/user/info auth path error")
+	}
+
+	if authFlag("/system/user") {
+		t.Error("/system/user auth path error")
+	}
+
+	if authFlag("/system/user/info") {
+		t.Error("/system/user/info auth path error")
+	}
+
+}
+
+func authPath(gfToken *gtoken.GfToken, t *testing.T) {
+	flag := gfToken.AuthPath("/user/info")
+	if flag {
+		t.Error("/user/info auth path error")
+	}
+
+	flag = gfToken.AuthPath("/system/user")
+	if flag {
+		t.Error("/system/user auth path error")
+	}
+
+	flag = gfToken.AuthPath("/system/user/info")
+	if flag {
+		t.Error("/system/user/info auth path error")
+	}
+
+	flag = gfToken.AuthPath("/system/dept")
+	if !flag {
+		t.Error("/system/dept auth path error")
+	}
+
+	flag = gfToken.AuthPath("/user/list")
+	if !flag {
+		t.Error("/user/list auth path error")
+	}
+
+	flag = gfToken.AuthPath("/user/add")
+	if !flag {
+		t.Error("/user/add auth path error")
+	}
+}
+
 func TestEncryptDecryptToken(t *testing.T) {
 	t.Log("encrypt and decrypt token test ")
 	gfToken := gtoken.GfToken{}