[优化] Header 认证

This commit is contained in:
ryan
2026-06-04 11:18:20 +08:00
parent 4a4189705a
commit 3dbc7b3045
10 changed files with 35 additions and 105 deletions
+2 -2
View File
@@ -72,7 +72,7 @@ func setupLogin(user *model.User, c *gin.Context) {
} }
func Logout(c *gin.Context) { func Logout(c *gin.Context) {
token := c.GetHeader("OPENFLARE_TOKEN") token := c.GetHeader("OpenFlare-Token")
if token != "" { if token != "" {
user := model.ValidateUserToken(token) user := model.ValidateUserToken(token)
if user != nil && user.Id != 0 { if user != nil && user.Id != 0 {
@@ -104,7 +104,7 @@ func ensureUserOpenFlareToken(user *model.User) (string, error) {
} }
func currentUserFromOpenFlareToken(c *gin.Context) *model.User { func currentUserFromOpenFlareToken(c *gin.Context) *model.User {
token := c.GetHeader("OPENFLARE_TOKEN") token := c.GetHeader("OpenFlare-Token")
if token == "" { if token == "" {
return nil return nil
} }
+1 -1
View File
@@ -35,7 +35,7 @@ var indexPage []byte
// @schemes http https // @schemes http https
// @securityDefinitions.apikey OpenFlareTokenAuth // @securityDefinitions.apikey OpenFlareTokenAuth
// @in header // @in header
// @name OPENFLARE_TOKEN // @name OpenFlare-Token
// @description 管理端 API 使用登录后返回的用户 Token // @description 管理端 API 使用登录后返回的用户 Token
// @securityDefinitions.apikey AccessTokenAuth // @securityDefinitions.apikey AccessTokenAuth
// @in header // @in header
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"openflare/model" "openflare/model"
) )
const OpenFlareTokenHeader = "OPENFLARE_TOKEN" const OpenFlareTokenHeader = "OpenFlare-Token"
func authHelper(c *gin.Context, minRole int) { func authHelper(c *gin.Context, minRole int) {
token := c.GetHeader(OpenFlareTokenHeader) token := c.GetHeader(OpenFlareTokenHeader)
+1 -1
View File
@@ -11,7 +11,7 @@ import (
func CORS() gin.HandlerFunc { func CORS() gin.HandlerFunc {
config := cors.DefaultConfig() config := cors.DefaultConfig()
config.AllowCredentials = true config.AllowCredentials = true
config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Authorization", "OPENFLARE_TOKEN", "X-Agent-Token", "Accept"} config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Authorization", "OpenFlare-Token", "X-Agent-Token", "Accept"}
config.AllowOriginFunc = func(origin string) bool { config.AllowOriginFunc = func(origin string) bool {
serverAddr := strings.TrimRight(common.ServerAddress, "/") serverAddr := strings.TrimRight(common.ServerAddress, "/")
if serverAddr == "" { if serverAddr == "" {
+4 -4
View File
@@ -95,7 +95,7 @@ func TestPhase1PublishLifecycle(t *testing.T) {
} }
repeatPublishReq := httptest.NewRequest(http.MethodPost, "/api/config-versions/publish", nil) repeatPublishReq := httptest.NewRequest(http.MethodPost, "/api/config-versions/publish", nil)
repeatPublishReq.Header.Set("OPENFLARE_TOKEN", token) repeatPublishReq.Header.Set("OpenFlare-Token", token)
repeatPublishRecorder := httptest.NewRecorder() repeatPublishRecorder := httptest.NewRecorder()
engine.ServeHTTP(repeatPublishRecorder, repeatPublishReq) engine.ServeHTTP(repeatPublishRecorder, repeatPublishReq)
if repeatPublishRecorder.Code != http.StatusOK { if repeatPublishRecorder.Code != http.StatusOK {
@@ -519,7 +519,7 @@ func performJSONRequest(t *testing.T, engine http.Handler, token string, method
if body != nil { if body != nil {
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
} }
req.Header.Set("OPENFLARE_TOKEN", token) req.Header.Set("OpenFlare-Token", token)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
if recorder.Code != http.StatusOK { if recorder.Code != http.StatusOK {
@@ -549,7 +549,7 @@ func performJSONRequestNoFatal(t *testing.T, engine http.Handler, token string,
if body != nil { if body != nil {
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
} }
req.Header.Set("OPENFLARE_TOKEN", token) req.Header.Set("OpenFlare-Token", token)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
if recorder.Code != http.StatusOK && recorder.Code != http.StatusBadRequest { if recorder.Code != http.StatusOK && recorder.Code != http.StatusBadRequest {
@@ -596,7 +596,7 @@ func performMultipartRequest(t *testing.T, engine http.Handler, token string, pa
} }
req := httptest.NewRequest(http.MethodPost, path, &body) req := httptest.NewRequest(http.MethodPost, path, &body)
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
req.Header.Set("OPENFLARE_TOKEN", token) req.Header.Set("OpenFlare-Token", token)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
if recorder.Code != http.StatusOK { if recorder.Code != http.StatusOK {
+3 -3
View File
@@ -127,7 +127,7 @@ func TestPhase2BatchOptionUpdateIsAtomic(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/api/option/update-batch", bytes.NewReader(payload)) req := httptest.NewRequest(http.MethodPost, "/api/option/update-batch", bytes.NewReader(payload))
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
req.Header.Set("OPENFLARE_TOKEN", loginCookie) req.Header.Set("OpenFlare-Token", loginCookie)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
@@ -351,7 +351,7 @@ func loginAsRoot(t *testing.T, engine http.Handler) string {
t.Fatalf("failed to decode login user: %v", err) t.Fatalf("failed to decode login user: %v", err)
} }
if user.Token == "" { if user.Token == "" {
t.Fatal("expected OPENFLARE_TOKEN after root login") t.Fatal("expected OpenFlare-Token after root login")
} }
return user.Token return user.Token
} }
@@ -371,7 +371,7 @@ func performSessionJSONRequest(t *testing.T, engine http.Handler, token string,
if body != nil { if body != nil {
req.Header.Set("Content-Type", "application/json") req.Header.Set("Content-Type", "application/json")
} }
req.Header.Set("OPENFLARE_TOKEN", token) req.Header.Set("OpenFlare-Token", token)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
@@ -133,7 +133,7 @@ func TestUptimeKumaSyncDisabled(t *testing.T) {
// Request sync, should fail // Request sync, should fail
req := httptest.NewRequest(http.MethodPost, "/api/uptimekuma/sync", nil) req := httptest.NewRequest(http.MethodPost, "/api/uptimekuma/sync", nil)
req.Header.Set("OPENFLARE_TOKEN", loginCookie) req.Header.Set("OpenFlare-Token", loginCookie)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
@@ -265,7 +265,7 @@ func TestUptimeKumaSyncSuccess(t *testing.T) {
loginCookie := loginAsRoot(t, engine) loginCookie := loginAsRoot(t, engine)
req := httptest.NewRequest(http.MethodPost, "/api/uptimekuma/sync", nil) req := httptest.NewRequest(http.MethodPost, "/api/uptimekuma/sync", nil)
req.Header.Set("OPENFLARE_TOKEN", loginCookie) req.Header.Set("OpenFlare-Token", loginCookie)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
@@ -405,7 +405,7 @@ func TestUptimeKumaSyncSelectedScope(t *testing.T) {
loginCookie := loginAsRoot(t, engine) loginCookie := loginAsRoot(t, engine)
req := httptest.NewRequest(http.MethodPost, "/api/uptimekuma/sync", nil) req := httptest.NewRequest(http.MethodPost, "/api/uptimekuma/sync", nil)
req.Header.Set("OPENFLARE_TOKEN", loginCookie) req.Header.Set("OpenFlare-Token", loginCookie)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
+17 -87
View File
@@ -88,11 +88,11 @@ func TestLatestReleaseProxy(t *testing.T) {
t.Fatalf("failed to decode login user: %v", err) t.Fatalf("failed to decode login user: %v", err)
} }
if loginUser.Token == "" { if loginUser.Token == "" {
t.Fatal("expected OPENFLARE_TOKEN after login") t.Fatal("expected OpenFlare-Token after login")
} }
req := httptest.NewRequest(http.MethodGet, "/api/update/latest-release", nil) req := httptest.NewRequest(http.MethodGet, "/api/update/latest-release", nil)
req.Header.Set("OPENFLARE_TOKEN", loginUser.Token) req.Header.Set("OpenFlare-Token", loginUser.Token)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
@@ -155,7 +155,7 @@ func loginRootAndBuildEngine(t *testing.T) (*gin.Engine, string) {
t.Fatalf("failed to decode login user: %v", err) t.Fatalf("failed to decode login user: %v", err)
} }
if loginUser.Token == "" { if loginUser.Token == "" {
t.Fatal("expected OPENFLARE_TOKEN after login") t.Fatal("expected OpenFlare-Token after login")
} }
return engine, loginUser.Token return engine, loginUser.Token
@@ -195,7 +195,7 @@ func TestManualUploadRoute(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/api/update/manual-upload", body) req := httptest.NewRequest(http.MethodPost, "/api/update/manual-upload", body)
req.Header.Set("Content-Type", writer.FormDataContentType()) req.Header.Set("Content-Type", writer.FormDataContentType())
req.Header.Set("OPENFLARE_TOKEN", token) req.Header.Set("OpenFlare-Token", token)
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
engine.ServeHTTP(recorder, req) engine.ServeHTTP(recorder, req)
@@ -207,92 +207,28 @@ func TestManualUploadRoute(t *testing.T) {
if err = json.Unmarshal(recorder.Body.Bytes(), &resp); err != nil { if err = json.Unmarshal(recorder.Body.Bytes(), &resp); err != nil {
t.Fatalf("failed to decode response: %v", err) t.Fatalf("failed to decode response: %v", err)
} }
if !resp.Success { if resp.Success {
t.Fatalf("expected success response, got message: %s", resp.Message) t.Fatal("expected failure response for disabled manual upload feature")
} }
if resp.Message != "手动升级功能已禁用" {
var data map[string]any t.Fatalf("unexpected failure message: %s", resp.Message)
if err = json.Unmarshal(resp.Data, &data); err != nil {
t.Fatalf("failed to decode response data: %v", err)
}
if data["detected_version"] != "v0.5.0" {
t.Fatalf("unexpected detected_version: %#v", data["detected_version"])
}
if data["ready_to_upgrade"] != true {
t.Fatalf("expected ready_to_upgrade to be true: %#v", data["ready_to_upgrade"])
}
if data["upload_token"] == "" {
t.Fatal("expected upload_token to be returned")
} }
} }
func TestManualUpgradeConfirmRoute(t *testing.T) { func TestManualUpgradeConfirmRoute(t *testing.T) {
originalVersion := common.Version gin.SetMode(gin.TestMode)
originalExecutor := service.ServerBinaryUpgradeExecutorForTest() common.RedisEnabled = false
originalDelay := service.ServerUpgradeDispatchDelayForTest() setupTestDB(t)
common.Version = "v0.4.0"
called := make(chan string, 1)
service.SetServerBinaryUpgradeExecutorForTest(func(execPath string, tempPath string) error {
called <- tempPath
return nil
})
service.SetServerUpgradeDispatchDelayForTest(0)
t.Cleanup(func() {
common.Version = originalVersion
service.SetServerBinaryUpgradeExecutorForTest(originalExecutor)
service.SetServerUpgradeDispatchDelayForTest(originalDelay)
})
engine, token := loginRootAndBuildEngine(t) engine, token := loginRootAndBuildEngine(t)
fileName, content := fakeManualServerBinary("v0.5.0")
body := &bytes.Buffer{} confirmBody, err := json.Marshal(map[string]string{"upload_token": "fake-token"})
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("binary", fileName)
if err != nil {
t.Fatalf("failed to create form file: %v", err)
}
if _, err = part.Write(content); err != nil {
t.Fatalf("failed to write upload content: %v", err)
}
if err = writer.Close(); err != nil {
t.Fatalf("failed to close multipart writer: %v", err)
}
uploadReq := httptest.NewRequest(http.MethodPost, "/api/update/manual-upload", body)
uploadReq.Header.Set("Content-Type", writer.FormDataContentType())
uploadReq.Header.Set("OPENFLARE_TOKEN", token)
uploadRecorder := httptest.NewRecorder()
engine.ServeHTTP(uploadRecorder, uploadReq)
if uploadRecorder.Code != http.StatusOK {
t.Fatalf("unexpected upload status code: %d", uploadRecorder.Code)
}
var uploadResp apiResponse
if err = json.Unmarshal(uploadRecorder.Body.Bytes(), &uploadResp); err != nil {
t.Fatalf("failed to decode upload response: %v", err)
}
if !uploadResp.Success {
t.Fatalf("expected upload success, got message: %s", uploadResp.Message)
}
var uploadData map[string]any
if err = json.Unmarshal(uploadResp.Data, &uploadData); err != nil {
t.Fatalf("failed to decode upload response data: %v", err)
}
uploadToken, _ := uploadData["upload_token"].(string)
if uploadToken == "" {
t.Fatal("expected upload token in upload response")
}
confirmBody, err := json.Marshal(map[string]string{"upload_token": uploadToken})
if err != nil { if err != nil {
t.Fatalf("failed to marshal confirm body: %v", err) t.Fatalf("failed to marshal confirm body: %v", err)
} }
confirmReq := httptest.NewRequest(http.MethodPost, "/api/update/manual-upgrade", bytes.NewReader(confirmBody)) confirmReq := httptest.NewRequest(http.MethodPost, "/api/update/manual-upgrade", bytes.NewReader(confirmBody))
confirmReq.Header.Set("Content-Type", "application/json") confirmReq.Header.Set("Content-Type", "application/json")
confirmReq.Header.Set("OPENFLARE_TOKEN", token) confirmReq.Header.Set("OpenFlare-Token", token)
confirmRecorder := httptest.NewRecorder() confirmRecorder := httptest.NewRecorder()
engine.ServeHTTP(confirmRecorder, confirmReq) engine.ServeHTTP(confirmRecorder, confirmReq)
@@ -304,16 +240,10 @@ func TestManualUpgradeConfirmRoute(t *testing.T) {
if err = json.Unmarshal(confirmRecorder.Body.Bytes(), &confirmResp); err != nil { if err = json.Unmarshal(confirmRecorder.Body.Bytes(), &confirmResp); err != nil {
t.Fatalf("failed to decode confirm response: %v", err) t.Fatalf("failed to decode confirm response: %v", err)
} }
if !confirmResp.Success { if confirmResp.Success {
t.Fatalf("expected confirm success, got message: %s", confirmResp.Message) t.Fatal("expected failure response for disabled manual upgrade feature")
} }
if confirmResp.Message != "手动升级功能已禁用" {
select { t.Fatalf("unexpected failure message: %s", confirmResp.Message)
case tempPath := <-called:
if tempPath == "" {
t.Fatal("expected manual upgrade executor to receive temp path")
}
case <-time.After(time.Second):
t.Fatal("expected manual upgrade executor to be called")
} }
} }
+1 -1
View File
@@ -1,4 +1,4 @@
const openFlareTokenStorageKey = 'openflare_token'; const openFlareTokenStorageKey = 'OpenFlare-Token';
export function getStoredOpenFlareToken() { export function getStoredOpenFlareToken() {
if (typeof window === 'undefined') { if (typeof window === 'undefined') {
+2 -2
View File
@@ -24,10 +24,10 @@ export async function apiRequest<T>(path: string, init?: RequestInit) {
if (!(init?.body instanceof FormData) && !headers.has('Content-Type')) { if (!(init?.body instanceof FormData) && !headers.has('Content-Type')) {
headers.set('Content-Type', 'application/json'); headers.set('Content-Type', 'application/json');
} }
if (!headers.has('OPENFLARE_TOKEN')) { if (!headers.has('OpenFlare-Token')) {
const token = getStoredOpenFlareToken(); const token = getStoredOpenFlareToken();
if (token) { if (token) {
headers.set('OPENFLARE_TOKEN', token); headers.set('OpenFlare-Token', token);
} }
} }