소스 검색

Improve kernel API authentication (#9702)

* :art: Add API `/api/network/echo`

* :art: Improve localhost checking

* :art: Add `model.CheckReadonly` for some APIs

/api/storage/setLocalStorage
/api/storage/setLocalStorageVal
/api/notebook/openNotebook
/api/notebook/removeNotebook
/api/search/removeTemplate
/api/attr/setBlockAttrs
/api/sync/importSyncProviderS3
/api/sync/importSyncProviderWebDAV
/api/riff/resetRiffCards
/api/snippet/setSnippet
/api/av/setAttributeViewBlockAttr
/api/archive/zip
/api/archive/unzip

* :art: Remove `model.CheckReadonly` for some APIs

/api/history/searchHistory
/api/history/getHistoryItems
/api/search/findReplace
/api/block/getParentNextChildID
/api/file/readDir
/api/sync/listCloudSyncDir
/api/asset/getDocImageAssets
/api/template/renderSprig
/api/ai/chatGPT
/api/ai/chatGPTWithAction

* :art: improve API `/api/network/echo`
Yingyi / 颖逸 1 년 전
부모
커밋
cb016aac14
5개의 변경된 파일110개의 추가작업 그리고 51개의 파일을 삭제
  1. 7 5
      app/src/protyle/util/compatibility.ts
  2. 65 0
      kernel/api/network.go
  3. 29 27
      kernel/api/router.go
  4. 3 1
      kernel/model/session.go
  5. 6 18
      kernel/util/net.go

+ 7 - 5
app/src/protyle/util/compatibility.ts

@@ -263,11 +263,13 @@ export const getLocalStorage = (cb: () => void) => {
         });
         cb();
 
-        // 数据兼容,移除历史数据,3.8.4 移除
-        fetchPost("/api/storage/removeLocalStorageVals", {
-            app: Constants.SIYUAN_APPID,
-            keys: ["leftColumn", "local-searchkey", "local-searchedata", "local-searchekeys", "local-searchetabdata", "rightColumn", "topBar"]
-        });
+        if (!window.siyuan.config.readonly) {
+            // 数据兼容,移除历史数据,3.8.4 移除
+            fetchPost("/api/storage/removeLocalStorageVals", {
+                app: Constants.SIYUAN_APPID,
+                keys: ["leftColumn", "local-searchkey", "local-searchedata", "local-searchekeys", "local-searchetabdata", "rightColumn", "topBar"]
+            });
+        }
     });
 };
 

+ 65 - 0
kernel/api/network.go

@@ -33,6 +33,71 @@ import (
 	"github.com/siyuan-note/siyuan/kernel/util"
 )
 
+func echo(c *gin.Context) {
+	ret := gulu.Ret.NewResult()
+	defer c.JSON(http.StatusOK, ret)
+
+	password, passwordSet := c.Request.URL.User.Password()
+
+	var rawData any
+	if data, err := c.GetRawData(); nil == err {
+		rawData = base64.StdEncoding.EncodeToString(data)
+	} else {
+		rawData = nil
+	}
+
+	ret.Data = map[string]interface{}{
+		"Context": map[string]interface{}{
+			"Params":       c.Params,
+			"HandlerNames": c.HandlerNames(),
+			"FullPath":     c.FullPath(),
+			"ClientIP":     c.ClientIP(),
+			"RemoteIP":     c.RemoteIP(),
+			"ContentType":  c.ContentType(),
+			"IsWebsocket":  c.IsWebsocket(),
+			"RawData":      rawData,
+		},
+		"Request": map[string]interface{}{
+			"Method":           c.Request.Method,
+			"URL":              c.Request.URL,
+			"Proto":            c.Request.Proto,
+			"ProtoMajor":       c.Request.ProtoMajor,
+			"ProtoMinor":       c.Request.ProtoMinor,
+			"Header":           c.Request.Header,
+			"ContentLength":    c.Request.ContentLength,
+			"TransferEncoding": c.Request.TransferEncoding,
+			"Close":            c.Request.Close,
+			"Host":             c.Request.Host,
+			"Form":             c.Request.Form,
+			"PostForm":         c.Request.PostForm,
+			"MultipartForm":    c.Request.MultipartForm,
+			"Trailer":          c.Request.Trailer,
+			"RemoteAddr":       c.Request.RemoteAddr,
+			"TLS":              c.Request.TLS,
+			"UserAgent":        c.Request.UserAgent(),
+			"Cookies":          c.Request.Cookies(),
+			"Referer":          c.Request.Referer(),
+		},
+		"URL": map[string]interface{}{
+			"EscapedPath":     c.Request.URL.EscapedPath(),
+			"EscapedFragment": c.Request.URL.EscapedFragment(),
+			"String":          c.Request.URL.String(),
+			"Redacted":        c.Request.URL.Redacted(),
+			"IsAbs":           c.Request.URL.IsAbs(),
+			"Query":           c.Request.URL.Query(),
+			"RequestURI":      c.Request.URL.RequestURI(),
+			"Hostname":        c.Request.URL.Hostname(),
+			"Port":            c.Request.URL.Port(),
+		},
+		"User": map[string]interface{}{
+			"Username":    c.Request.URL.User.Username(),
+			"Password":    password,
+			"PasswordSet": passwordSet,
+			"String":      c.Request.URL.User.String(),
+		},
+	}
+}
+
 func forwardProxy(c *gin.Context) {
 	ret := gulu.Ret.NewResult()
 	defer c.JSON(http.StatusOK, ret)

+ 29 - 27
kernel/api/router.go

@@ -33,6 +33,9 @@ func ServeAPI(ginServer *gin.Engine) {
 	ginServer.Handle("POST", "/api/system/loginAuth", model.LoginAuth)
 	ginServer.Handle("POST", "/api/system/logoutAuth", model.LogoutAuth)
 	ginServer.Handle("GET", "/api/system/getCaptcha", model.GetCaptcha)
+	ginServer.Handle("POST", "/api/system/setUILayout", setUILayout) // 这里不加鉴权 After modifying the access authentication code on the browser side, the other side does not refresh https://github.com/siyuan-note/siyuan/issues/8028
+
+	ginServer.Handle("GET", "/snippets/*filepath", serveSnippets)
 
 	// 需要鉴权
 
@@ -56,15 +59,14 @@ func ServeAPI(ginServer *gin.Engine) {
 	ginServer.Handle("POST", "/api/system/setAppearanceMode", model.CheckAuth, setAppearanceMode)
 	ginServer.Handle("POST", "/api/system/getSysFonts", model.CheckAuth, getSysFonts)
 	ginServer.Handle("POST", "/api/system/exit", model.CheckAuth, exit)
-	ginServer.Handle("POST", "/api/system/setUILayout", setUILayout) // 这里不加鉴权 After modifying the access authentication code on the browser side, the other side does not refresh https://github.com/siyuan-note/siyuan/issues/8028
 	ginServer.Handle("POST", "/api/system/getConf", model.CheckAuth, getConf)
 	ginServer.Handle("POST", "/api/system/checkUpdate", model.CheckAuth, checkUpdate)
 	ginServer.Handle("POST", "/api/system/exportLog", model.CheckAuth, exportLog)
 	ginServer.Handle("POST", "/api/system/getChangelog", model.CheckAuth, getChangelog)
 
-	ginServer.Handle("POST", "/api/storage/setLocalStorage", model.CheckAuth, setLocalStorage)
+	ginServer.Handle("POST", "/api/storage/setLocalStorage", model.CheckAuth, model.CheckReadonly, setLocalStorage)
 	ginServer.Handle("POST", "/api/storage/getLocalStorage", model.CheckAuth, getLocalStorage)
-	ginServer.Handle("POST", "/api/storage/setLocalStorageVal", model.CheckAuth, setLocalStorageVal)
+	ginServer.Handle("POST", "/api/storage/setLocalStorageVal", model.CheckAuth, model.CheckReadonly, setLocalStorageVal)
 	ginServer.Handle("POST", "/api/storage/removeLocalStorageVals", model.CheckAuth, model.CheckReadonly, removeLocalStorageVals)
 	ginServer.Handle("POST", "/api/storage/setCriterion", model.CheckAuth, model.CheckReadonly, setCriterion)
 	ginServer.Handle("POST", "/api/storage/getCriteria", model.CheckAuth, getCriteria)
@@ -78,12 +80,12 @@ func ServeAPI(ginServer *gin.Engine) {
 	ginServer.Handle("POST", "/api/account/startFreeTrial", model.CheckAuth, model.CheckReadonly, startFreeTrial)
 
 	ginServer.Handle("POST", "/api/notebook/lsNotebooks", model.CheckAuth, lsNotebooks)
-	ginServer.Handle("POST", "/api/notebook/openNotebook", model.CheckAuth, openNotebook)
+	ginServer.Handle("POST", "/api/notebook/openNotebook", model.CheckAuth, model.CheckReadonly, openNotebook)
 	ginServer.Handle("POST", "/api/notebook/closeNotebook", model.CheckAuth, model.CheckReadonly, closeNotebook)
 	ginServer.Handle("POST", "/api/notebook/getNotebookConf", model.CheckAuth, getNotebookConf)
 	ginServer.Handle("POST", "/api/notebook/setNotebookConf", model.CheckAuth, model.CheckReadonly, setNotebookConf)
 	ginServer.Handle("POST", "/api/notebook/createNotebook", model.CheckAuth, model.CheckReadonly, createNotebook)
-	ginServer.Handle("POST", "/api/notebook/removeNotebook", model.CheckAuth, removeNotebook)
+	ginServer.Handle("POST", "/api/notebook/removeNotebook", model.CheckAuth, model.CheckReadonly, removeNotebook)
 	ginServer.Handle("POST", "/api/notebook/renameNotebook", model.CheckAuth, model.CheckReadonly, renameNotebook)
 	ginServer.Handle("POST", "/api/notebook/changeSortNotebook", model.CheckAuth, model.CheckReadonly, changeSortNotebook)
 	ginServer.Handle("POST", "/api/notebook/setNotebookIcon", model.CheckAuth, model.CheckReadonly, setNotebookIcon)
@@ -122,8 +124,8 @@ func ServeAPI(ginServer *gin.Engine) {
 	ginServer.Handle("POST", "/api/history/rollbackDocHistory", model.CheckAuth, model.CheckReadonly, rollbackDocHistory)
 	ginServer.Handle("POST", "/api/history/clearWorkspaceHistory", model.CheckAuth, model.CheckReadonly, clearWorkspaceHistory)
 	ginServer.Handle("POST", "/api/history/reindexHistory", model.CheckAuth, model.CheckReadonly, reindexHistory)
-	ginServer.Handle("POST", "/api/history/searchHistory", model.CheckAuth, model.CheckReadonly, searchHistory)
-	ginServer.Handle("POST", "/api/history/getHistoryItems", model.CheckAuth, model.CheckReadonly, getHistoryItems)
+	ginServer.Handle("POST", "/api/history/searchHistory", model.CheckAuth, searchHistory)
+	ginServer.Handle("POST", "/api/history/getHistoryItems", model.CheckAuth, getHistoryItems)
 
 	ginServer.Handle("POST", "/api/outline/getDocOutline", model.CheckAuth, getDocOutline)
 	ginServer.Handle("POST", "/api/bookmark/getBookmark", model.CheckAuth, getBookmark)
@@ -141,14 +143,14 @@ func ServeAPI(ginServer *gin.Engine) {
 
 	ginServer.Handle("POST", "/api/search/searchTag", model.CheckAuth, searchTag)
 	ginServer.Handle("POST", "/api/search/searchTemplate", model.CheckAuth, searchTemplate)
-	ginServer.Handle("POST", "/api/search/removeTemplate", model.CheckAuth, removeTemplate)
+	ginServer.Handle("POST", "/api/search/removeTemplate", model.CheckAuth, model.CheckReadonly, removeTemplate)
 	ginServer.Handle("POST", "/api/search/searchWidget", model.CheckAuth, searchWidget)
 	ginServer.Handle("POST", "/api/search/searchRefBlock", model.CheckAuth, searchRefBlock)
 	ginServer.Handle("POST", "/api/search/searchEmbedBlock", model.CheckAuth, searchEmbedBlock)
 	ginServer.Handle("POST", "/api/search/getEmbedBlock", model.CheckAuth, getEmbedBlock)
 	ginServer.Handle("POST", "/api/search/fullTextSearchBlock", model.CheckAuth, fullTextSearchBlock)
 	ginServer.Handle("POST", "/api/search/searchAsset", model.CheckAuth, searchAsset)
-	ginServer.Handle("POST", "/api/search/findReplace", model.CheckAuth, model.CheckReadonly, findReplace)
+	ginServer.Handle("POST", "/api/search/findReplace", model.CheckAuth, findReplace)
 	ginServer.Handle("POST", "/api/search/fullTextSearchAssetContent", model.CheckAuth, fullTextSearchAssetContent)
 	ginServer.Handle("POST", "/api/search/getAssetContent", model.CheckAuth, getAssetContent)
 
@@ -183,14 +185,14 @@ func ServeAPI(ginServer *gin.Engine) {
 	ginServer.Handle("POST", "/api/block/getHeadingChildrenDOM", model.CheckAuth, getHeadingChildrenDOM)
 	ginServer.Handle("POST", "/api/block/swapBlockRef", model.CheckAuth, model.CheckReadonly, swapBlockRef)
 	ginServer.Handle("POST", "/api/block/transferBlockRef", model.CheckAuth, model.CheckReadonly, transferBlockRef)
-	ginServer.Handle("POST", "/api/block/getParentNextChildID", model.CheckAuth, model.CheckReadonly, getParentNextChildID)
+	ginServer.Handle("POST", "/api/block/getParentNextChildID", model.CheckAuth, getParentNextChildID)
 
 	ginServer.Handle("POST", "/api/file/getFile", model.CheckAuth, getFile)
 	ginServer.Handle("POST", "/api/file/putFile", model.CheckAuth, model.CheckReadonly, putFile)
 	ginServer.Handle("POST", "/api/file/copyFile", model.CheckAuth, model.CheckReadonly, copyFile)
 	ginServer.Handle("POST", "/api/file/removeFile", model.CheckAuth, model.CheckReadonly, removeFile)
 	ginServer.Handle("POST", "/api/file/renameFile", model.CheckAuth, model.CheckReadonly, renameFile)
-	ginServer.Handle("POST", "/api/file/readDir", model.CheckAuth, model.CheckReadonly, readDir)
+	ginServer.Handle("POST", "/api/file/readDir", model.CheckAuth, readDir)
 
 	ginServer.Handle("POST", "/api/ref/refreshBacklink", model.CheckAuth, refreshBacklink)
 	ginServer.Handle("POST", "/api/ref/getBacklink", model.CheckAuth, getBacklink)
@@ -200,7 +202,7 @@ func ServeAPI(ginServer *gin.Engine) {
 
 	ginServer.Handle("POST", "/api/attr/getBookmarkLabels", model.CheckAuth, getBookmarkLabels)
 	ginServer.Handle("POST", "/api/attr/resetBlockAttrs", model.CheckAuth, model.CheckReadonly, resetBlockAttrs)
-	ginServer.Handle("POST", "/api/attr/setBlockAttrs", model.CheckAuth, setBlockAttrs)
+	ginServer.Handle("POST", "/api/attr/setBlockAttrs", model.CheckAuth, model.CheckReadonly, setBlockAttrs)
 	ginServer.Handle("POST", "/api/attr/getBlockAttrs", model.CheckAuth, getBlockAttrs)
 
 	ginServer.Handle("POST", "/api/cloud/getCloudSpace", model.CheckAuth, getCloudSpace)
@@ -215,15 +217,15 @@ func ServeAPI(ginServer *gin.Engine) {
 	ginServer.Handle("POST", "/api/sync/setCloudSyncDir", model.CheckAuth, model.CheckReadonly, setCloudSyncDir)
 	ginServer.Handle("POST", "/api/sync/createCloudSyncDir", model.CheckAuth, model.CheckReadonly, createCloudSyncDir)
 	ginServer.Handle("POST", "/api/sync/removeCloudSyncDir", model.CheckAuth, model.CheckReadonly, removeCloudSyncDir)
-	ginServer.Handle("POST", "/api/sync/listCloudSyncDir", model.CheckAuth, model.CheckReadonly, listCloudSyncDir)
+	ginServer.Handle("POST", "/api/sync/listCloudSyncDir", model.CheckAuth, listCloudSyncDir)
 	ginServer.Handle("POST", "/api/sync/performSync", model.CheckAuth, model.CheckReadonly, performSync)
 	ginServer.Handle("POST", "/api/sync/performBootSync", model.CheckAuth, model.CheckReadonly, performBootSync)
 	ginServer.Handle("POST", "/api/sync/getBootSync", model.CheckAuth, getBootSync)
 	ginServer.Handle("POST", "/api/sync/getSyncInfo", model.CheckAuth, getSyncInfo)
 	ginServer.Handle("POST", "/api/sync/exportSyncProviderS3", model.CheckAuth, exportSyncProviderS3)
-	ginServer.Handle("POST", "/api/sync/importSyncProviderS3", model.CheckAuth, importSyncProviderS3)
+	ginServer.Handle("POST", "/api/sync/importSyncProviderS3", model.CheckAuth, model.CheckReadonly, importSyncProviderS3)
 	ginServer.Handle("POST", "/api/sync/exportSyncProviderWebDAV", model.CheckAuth, exportSyncProviderWebDAV)
-	ginServer.Handle("POST", "/api/sync/importSyncProviderWebDAV", model.CheckAuth, importSyncProviderWebDAV)
+	ginServer.Handle("POST", "/api/sync/importSyncProviderWebDAV", model.CheckAuth, model.CheckReadonly, importSyncProviderWebDAV)
 
 	ginServer.Handle("POST", "/api/inbox/getShorthands", model.CheckAuth, getShorthands)
 	ginServer.Handle("POST", "/api/inbox/getShorthand", model.CheckAuth, getShorthand)
@@ -243,7 +245,7 @@ func ServeAPI(ginServer *gin.Engine) {
 	ginServer.Handle("POST", "/api/asset/getMissingAssets", model.CheckAuth, getMissingAssets)
 	ginServer.Handle("POST", "/api/asset/removeUnusedAsset", model.CheckAuth, model.CheckReadonly, removeUnusedAsset)
 	ginServer.Handle("POST", "/api/asset/removeUnusedAssets", model.CheckAuth, model.CheckReadonly, removeUnusedAssets)
-	ginServer.Handle("POST", "/api/asset/getDocImageAssets", model.CheckAuth, model.CheckReadonly, getDocImageAssets)
+	ginServer.Handle("POST", "/api/asset/getDocImageAssets", model.CheckAuth, getDocImageAssets)
 	ginServer.Handle("POST", "/api/asset/renameAsset", model.CheckAuth, model.CheckReadonly, renameAsset)
 	ginServer.Handle("POST", "/api/asset/getImageOCRText", model.CheckAuth, model.CheckReadonly, getImageOCRText)
 	ginServer.Handle("POST", "/api/asset/setImageOCRText", model.CheckAuth, model.CheckReadonly, setImageOCRText)
@@ -284,7 +286,7 @@ func ServeAPI(ginServer *gin.Engine) {
 
 	ginServer.Handle("POST", "/api/template/render", model.CheckAuth, renderTemplate)
 	ginServer.Handle("POST", "/api/template/docSaveAsTemplate", model.CheckAuth, model.CheckReadonly, docSaveAsTemplate)
-	ginServer.Handle("POST", "/api/template/renderSprig", model.CheckAuth, model.CheckReadonly, renderSprig)
+	ginServer.Handle("POST", "/api/template/renderSprig", model.CheckAuth, renderSprig)
 
 	ginServer.Handle("POST", "/api/transactions", model.CheckAuth, model.CheckReadonly, performTransactions)
 
@@ -363,33 +365,33 @@ func ServeAPI(ginServer *gin.Engine) {
 	ginServer.Handle("POST", "/api/riff/getRiffCards", model.CheckAuth, getRiffCards)
 	ginServer.Handle("POST", "/api/riff/getTreeRiffCards", model.CheckAuth, getTreeRiffCards)
 	ginServer.Handle("POST", "/api/riff/getNotebookRiffCards", model.CheckAuth, getNotebookRiffCards)
-	ginServer.Handle("POST", "/api/riff/resetRiffCards", model.CheckAuth, resetRiffCards)
+	ginServer.Handle("POST", "/api/riff/resetRiffCards", model.CheckAuth, model.CheckReadonly, resetRiffCards)
 
 	ginServer.Handle("POST", "/api/notification/pushMsg", model.CheckAuth, pushMsg)
 	ginServer.Handle("POST", "/api/notification/pushErrMsg", model.CheckAuth, pushErrMsg)
 
 	ginServer.Handle("POST", "/api/snippet/getSnippet", model.CheckAuth, getSnippet)
-	ginServer.Handle("POST", "/api/snippet/setSnippet", model.CheckAuth, setSnippet)
+	ginServer.Handle("POST", "/api/snippet/setSnippet", model.CheckAuth, model.CheckReadonly, setSnippet)
 	ginServer.Handle("POST", "/api/snippet/removeSnippet", model.CheckAuth, model.CheckReadonly, removeSnippet)
-	ginServer.Handle("GET", "/snippets/*filepath", serveSnippets)
 
 	ginServer.Handle("POST", "/api/av/renderAttributeView", model.CheckAuth, renderAttributeView)
 	ginServer.Handle("POST", "/api/av/getAttributeViewKeys", model.CheckAuth, getAttributeViewKeys)
-	ginServer.Handle("POST", "/api/av/setAttributeViewBlockAttr", model.CheckAuth, setAttributeViewBlockAttr)
+	ginServer.Handle("POST", "/api/av/setAttributeViewBlockAttr", model.CheckAuth, model.CheckReadonly, setAttributeViewBlockAttr)
 
-	ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, model.CheckReadonly, chatGPT)
-	ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, model.CheckReadonly, chatGPTWithAction)
+	ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, chatGPT)
+	ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, chatGPTWithAction)
 
 	ginServer.Handle("POST", "/api/petal/loadPetals", model.CheckAuth, loadPetals)
 	ginServer.Handle("POST", "/api/petal/setPetalEnabled", model.CheckAuth, model.CheckReadonly, setPetalEnabled)
 
-	ginServer.Handle("POST", "/api/network/forwardProxy", model.CheckAuth, model.CheckReadonly, forwardProxy)
+	ginServer.Any("/api/network/echo", model.CheckAuth, echo)
+	ginServer.Handle("POST", "/api/network/forwardProxy", model.CheckAuth, forwardProxy)
 
 	ginServer.Handle("GET", "/ws/broadcast", model.CheckAuth, broadcast)
-	ginServer.Handle("GET", "/api/broadcast/channels", model.CheckAuth, getChannels)
 	ginServer.Handle("POST", "/api/broadcast/postMessage", model.CheckAuth, postMessage)
+	ginServer.Handle("POST", "/api/broadcast/getChannels", model.CheckAuth, getChannels)
 	ginServer.Handle("POST", "/api/broadcast/getChannelInfo", model.CheckAuth, getChannelInfo)
 
-	ginServer.Handle("POST", "/api/archive/zip", model.CheckAuth, zip)
-	ginServer.Handle("POST", "/api/archive/unzip", model.CheckAuth, unzip)
+	ginServer.Handle("POST", "/api/archive/zip", model.CheckAuth, model.CheckReadonly, zip)
+	ginServer.Handle("POST", "/api/archive/unzip", model.CheckAuth, model.CheckReadonly, unzip)
 }

+ 3 - 1
kernel/model/session.go

@@ -163,10 +163,12 @@ func CheckAuth(c *gin.Context) {
 	// 未设置访问授权码
 	if "" == Conf.AccessAuthCode {
 		// Authenticate requests with the Origin header other than 127.0.0.1 https://github.com/siyuan-note/siyuan/issues/9180
+		clientIP := c.ClientIP()
 		host := c.GetHeader("Host")
 		origin := c.GetHeader("Origin")
 		forwardedHost := c.GetHeader("X-Forwarded-Host")
 		if !localhost ||
+			("" != clientIP && !util.IsLocalHostname(clientIP)) ||
 			("" != host && !util.IsLocalHost(host)) ||
 			("" != origin && !util.IsLocalOrigin(origin) && !strings.HasPrefix(origin, "chrome-extension://")) ||
 			("" != forwardedHost && !util.IsLocalHost(forwardedHost)) {
@@ -243,7 +245,7 @@ func CheckAuth(c *gin.Context) {
 	if workspaceSession.AccessAuthCode != Conf.AccessAuthCode {
 		userAgentHeader := c.GetHeader("User-Agent")
 		if strings.HasPrefix(userAgentHeader, "SiYuan/") || strings.HasPrefix(userAgentHeader, "Mozilla/") {
-			if "GET" != c.Request.Method {
+			if "GET" != c.Request.Method || c.IsWebsocket() {
 				c.JSON(http.StatusUnauthorized, map[string]interface{}{"code": -1, "msg": Conf.Language(156)})
 				c.Abort()
 				return

+ 6 - 18
kernel/util/net.go

@@ -47,23 +47,8 @@ func ValidOptionalPort(port string) bool {
 	return true
 }
 
-func SplitHost(host string) (hostname, port string) {
-	hostname = host
-
-	colon := strings.LastIndexByte(hostname, ':')
-	if colon != -1 && ValidOptionalPort(hostname[colon:]) {
-		hostname, port = hostname[:colon], hostname[colon+1:]
-	}
-
-	if strings.HasPrefix(hostname, "[") && strings.HasSuffix(hostname, "]") {
-		hostname = hostname[1 : len(hostname)-1]
-	}
-
-	return
-}
-
 func IsLocalHostname(hostname string) bool {
-	if "localhost" == hostname {
+	if "localhost" == hostname || strings.HasSuffix(hostname, ".localhost") {
 		return true
 	}
 	if ip := net.ParseIP(hostname); nil != ip {
@@ -73,8 +58,11 @@ func IsLocalHostname(hostname string) bool {
 }
 
 func IsLocalHost(host string) bool {
-	hostname, _ := SplitHost(host)
-	return IsLocalHostname(hostname)
+	if hostname, _, err := net.SplitHostPort(strings.TrimSpace(host)); nil != err {
+		return false
+	} else {
+		return IsLocalHostname(hostname)
+	}
 }
 
 func IsLocalOrigin(origin string) bool {