Repository: GopeedLab/gopeed

Issue:
1.6版本 web界面无API token配置地方，同时通讯协议也无法配置
如图：
![Image](https://github.com/user-attachments/assets/7dd90857-a724-401e-9388-3ae584bd2a24)

Hints from issue comments:
@monkeyWie: web版需要启动的时候指定，具体参考：https://docs.gopeed.com/zh/install.html#web-%E9%85%8D%E7%BD%AE@nowaits: 在配置文件里和cli都设置定apitoken，用Gopeed浏览器插件接入下载，添加远程下载服务器，不管哪个口令都能测试成功，也能正常下载，所以口令没生效
以下是两次设置随意长度的token，都成功了，都能进行后续的下载
![Image](https://github.com/user-attachments/assets/c904eced-248f-41cc-acd2-f09797b63e9d)

![Image](https://github.com/user-attachments/assets/be67e817-3965-491d-a1c7-cad2282fd061)@monkeyWie: 我这里没有复现哦，启动命令：
```
./gopeed -T 123456
```@nowaits: 以下是配置：
```
root@ubuntu:~/nas/tools# cat gopeed-config.json 
{
    "username": "goloader",
    "password": "1qaz1234",
    "address": "0.0.0.0",
    "port": 8097,
    "apiToken":"1qaz1234",
    "storageDir": "/root/download",
        "downloadConfig": {
            "downloadDir": "/root/download"
          }
}
root@ubuntu:~/nas/tools# ./gopeed-web-v1.6.10-linux-arm64/gopeed -c ./gopeed-config.json
```@monkeyWie: 还是没有复现，浏览器访问看看有没有认证弹窗@nowaits: 浏览器第一次打开gopeed web界面有用户名密码弹窗认证，成功之后，就不再认证了@monkeyWie: 那应该没问题，直接在服务器上用curl试试token看看呢:

```
curl -H "X-Api-Token: test" localhost:8097/api/v1/info
curl -H "X-Api-Token: 1qaz1234" localhost:8097/api/v1/info
```@nowaits: ```
root@ubuntu:~# curl -H "X-Api-Token: test" localhost:8097/api/v1/info
{"code":1001,"msg":"unauthorized","data":null}
root@ubuntu:~# curl -H "X-Api-Token: 1qaz1234" localhost:8097/api/v1/info
{"code":0,"msg":"","data":{"arch":"arm64","inDocker":false,"os":"linux","runtime":"go1.23.6","version":"1.6.10"}}
root@ubuntu:~# 
```@monkeyWie: 说明token是生效了啊，估计是api check那边带上了浏览器basic鉴权token所以就通过了@nowaits: 第一次插件随机密码
![Image](https://github.com/user-attachments/assets/30234f47-a123-4e58-8ff0-6b09c9ac9f53)
![Image](https://github.com/user-attachments/assets/01a2a5b1-8a1c-42c3-8e58-bb6bf5a9f2a6)
第二次插件随机密码
![Image](https://github.com/user-attachments/assets/34fc52ed-ebd9-4e16-a417-2a8625681b3a)
![Image](https://github.com/user-attachments/assets/4b685899-8ec1-4815-9b65-137fb0ef4534)

应该是插件认证的问题，与curl机制不一样，绕过了认证@monkeyWie: 因为浏览器那边你输入了账号密码登录，这个也是一种验证方式，浏览器会带着这个登录token去测试，我这可以小优化下，如果api token和login token同时传递的话只校验api token就好了@nowaits: 那应该是这个原因@nowaits: web版本的api token无法配置，也瞅一下吧，现在只能在配置文件里改

Solution patch:
diff --git a/pkg/rest/server.go b/pkg/rest/server.go
index 1e4a10e..0d7c4dc 100644
--- a/pkg/rest/server.go
+++ b/pkg/rest/server.go
@@ -131,13 +131,25 @@ func BuildServer(startCfg *model.StartConfig) (*http.Server, net.Listener, error
 		r.PathPrefix("/").Handler(gzipMiddleware(http.FileServer(newEmbedCacheFileSystem(http.FS(startCfg.WebFS)))))
 	}
 
-	if startCfg.ApiToken != "" || (startCfg.WebEnable && startCfg.WebBasicAuth != nil) {
+	enableApiToken := startCfg.ApiToken != ""
+	enableBasicAuth := startCfg.WebEnable && startCfg.WebBasicAuth != nil
+	if enableApiToken || enableBasicAuth {
 		r.Use(func(h http.Handler) http.Handler {
 			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-				if startCfg.ApiToken != "" && r.Header.Get("X-Api-Token") == startCfg.ApiToken {
-					h.ServeHTTP(w, r)
-					return
+				if enableApiToken {
+					apiTokenHeader := r.Header["X-Api-Token"]
+					// If api token header is set, only check api token ignore basic auth
+					if len(apiTokenHeader) > 0 {
+						if apiTokenHeader[0] == startCfg.ApiToken {
+							h.ServeHTTP(w, r)
+							return
+						}
+
+						WriteStatusJson(w, http.StatusUnauthorized, model.NewErrorResult("unauthorized", model.CodeUnauthorized))
+						return
+					}
 				}
+
 				if startCfg.WebEnable && startCfg.WebBasicAuth != nil {
 					if r.Header.Get("Authorization") == startCfg.WebBasicAuth.Authorization() {
 						h.ServeHTTP(w, r)



Test patch:
diff --git a/pkg/rest/server_test.go b/pkg/rest/server_test.go
index 9dee79f..6e2e922 100644
--- a/pkg/rest/server_test.go
+++ b/pkg/rest/server_test.go
@@ -625,6 +625,22 @@ func TestAuthorization(t *testing.T) {
 	if status != http.StatusOK {
 		t.Errorf("TestAuthorization() got = %v, want %v", status, http.StatusOK)
 	}
+
+	status, _ = doHttpRequest0(http.MethodGet, "/api/v1/config", map[string]string{
+		"Authorization": cfg.WebBasicAuth.Authorization(),
+		"X-Api-Token":   cfg.ApiToken,
+	}, nil)
+	if status != http.StatusOK {
+		t.Errorf("TestAuthorization() got = %v, want %v", status, http.StatusOK)
+	}
+
+	status, _ = doHttpRequest0(http.MethodGet, "/api/v1/config", map[string]string{
+		"Authorization": cfg.WebBasicAuth.Authorization(),
+		"X-Api-Token":   "",
+	}, nil)
+	if status != http.StatusUnauthorized {
+		t.Errorf("TestAuthorization() got = %v, want %v", status, http.StatusUnauthorized)
+	}
 }
 
 func doTest(handler func()) {