mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-12-16 14:40:12 +01:00
🎨 Add cookie-based auth in publish proxy (#15692)
* chore(publish-auth): Add TODO for cookie-based auth in publish proxy A TODO comment was added to indicate future implementation of authentication using cookies in the PublishServiceTransport RoundTrip method. * 🎨 Add session-based authentication for publish proxy Introduces session management using cookies for the publish reverse proxy server. Adds session ID generation, storage, and validation in kernel/model/auth.go, and updates the proxy transport to check for valid sessions before falling back to basic authentication. Sets a session cookie upon successful basic auth login. * 🐛 Fixed the issue of repeatedly setting cookies * 🎨 Dynamically remove invalid session IDs * ♻️ Revert changes in pnpm-lock.yaml
This commit is contained in:
parent
2a4adf089f
commit
ff4d215f78
3 changed files with 67 additions and 7 deletions
|
|
@ -37,6 +37,7 @@ require (
|
||||||
github.com/go-ole/go-ole v1.3.0
|
github.com/go-ole/go-ole v1.3.0
|
||||||
github.com/gofrs/flock v0.12.1
|
github.com/gofrs/flock v0.12.1
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.2
|
github.com/golang-jwt/jwt/v5 v5.2.2
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/css v1.0.1
|
github.com/gorilla/css v1.0.1
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/imroc/req/v3 v3.54.2
|
github.com/imroc/req/v3 v3.54.2
|
||||||
|
|
@ -129,7 +130,6 @@ require (
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
|
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
github.com/gopherjs/gopherjs v1.17.2 // indirect
|
||||||
github.com/gorilla/context v1.1.2 // indirect
|
github.com/gorilla/context v1.1.2 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,10 @@ package model
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/siyuan-note/logging"
|
"github.com/siyuan-note/logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -29,12 +31,15 @@ type Account struct {
|
||||||
Password string
|
Password string
|
||||||
Token string
|
Token string
|
||||||
}
|
}
|
||||||
type AccountsMap map[string]*Account
|
type AccountsMap map[string]*Account // username -> account
|
||||||
|
type SessionsMap map[string]string // sessionID -> username
|
||||||
type ClaimsKeyType string
|
type ClaimsKeyType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
XAuthTokenKey = "X-Auth-Token"
|
XAuthTokenKey = "X-Auth-Token"
|
||||||
|
|
||||||
|
SessionIdCookieName = "publish-visitor-session-id"
|
||||||
|
|
||||||
ClaimsContextKey = "claims"
|
ClaimsContextKey = "claims"
|
||||||
|
|
||||||
iss = "siyuan-publish-reverse-proxy-server"
|
iss = "siyuan-publish-reverse-proxy-server"
|
||||||
|
|
@ -46,13 +51,37 @@ const (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
accountsMap = AccountsMap{}
|
accountsMap = AccountsMap{}
|
||||||
jwtKey = make([]byte, 32)
|
sessionsMap = SessionsMap{}
|
||||||
|
sessionLock = sync.Mutex{}
|
||||||
|
|
||||||
|
jwtKey = make([]byte, 32)
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetBasicAuthAccount(username string) *Account {
|
func GetBasicAuthAccount(username string) *Account {
|
||||||
return accountsMap[username]
|
return accountsMap[username]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetBasicAuthUsernameBySessionID(sessionID string) string {
|
||||||
|
return sessionsMap[sessionID]
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNewSessionID() string {
|
||||||
|
sessionID := uuid.New().String()
|
||||||
|
return sessionID
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddSession(sessionID, username string) {
|
||||||
|
sessionLock.Lock()
|
||||||
|
defer sessionLock.Unlock()
|
||||||
|
sessionsMap[sessionID] = username
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteSession(sessionID string) {
|
||||||
|
sessionLock.Lock()
|
||||||
|
defer sessionLock.Unlock()
|
||||||
|
delete(sessionsMap, sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
func InitAccounts() {
|
func InitAccounts() {
|
||||||
accountsMap = AccountsMap{
|
accountsMap = AccountsMap{
|
||||||
"": &Account{}, // 匿名用户
|
"": &Account{}, // 匿名用户
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,28 @@ func rewrite(r *httputil.ProxyRequest) {
|
||||||
|
|
||||||
func (PublishServiceTransport) RoundTrip(request *http.Request) (response *http.Response, err error) {
|
func (PublishServiceTransport) RoundTrip(request *http.Request) (response *http.Response, err error) {
|
||||||
if model.Conf.Publish.Auth.Enable {
|
if model.Conf.Publish.Auth.Enable {
|
||||||
|
// Session Auth
|
||||||
|
sessionIdCookie, cookieErr := request.Cookie(model.SessionIdCookieName)
|
||||||
|
if cookieErr == nil {
|
||||||
|
// Check session ID
|
||||||
|
sessionID := sessionIdCookie.Value
|
||||||
|
if username := model.GetBasicAuthUsernameBySessionID(sessionID); username != "" {
|
||||||
|
// Valid session
|
||||||
|
if account := model.GetBasicAuthAccount(username); account != nil {
|
||||||
|
// Valid account
|
||||||
|
request.Header.Set(model.XAuthTokenKey, account.Token)
|
||||||
|
response, err = http.DefaultTransport.RoundTrip(request)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
// Invalid account, remove session
|
||||||
|
model.DeleteSession(sessionID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Basic Auth
|
// Basic Auth
|
||||||
username, password, ok := request.BasicAuth()
|
username, password, ok := request.BasicAuth()
|
||||||
account := model.GetBasicAuthAccount(username)
|
account := model.GetBasicAuthAccount(username)
|
||||||
|
|
||||||
if !ok ||
|
if !ok ||
|
||||||
account == nil ||
|
account == nil ||
|
||||||
account.Username == "" || // 匿名用户
|
account.Username == "" || // 匿名用户
|
||||||
|
|
@ -149,13 +167,26 @@ func (PublishServiceTransport) RoundTrip(request *http.Request) (response *http.
|
||||||
ContentLength: -1,
|
ContentLength: -1,
|
||||||
}, nil
|
}, nil
|
||||||
} else {
|
} else {
|
||||||
|
// set session cookie
|
||||||
|
sessionID := model.GetNewSessionID()
|
||||||
|
cookie := &http.Cookie{
|
||||||
|
Name: model.SessionIdCookieName,
|
||||||
|
Value: sessionID,
|
||||||
|
Path: "/",
|
||||||
|
HttpOnly: true,
|
||||||
|
}
|
||||||
|
model.AddSession(sessionID, username)
|
||||||
|
|
||||||
// set JWT
|
// set JWT
|
||||||
request.Header.Set(model.XAuthTokenKey, account.Token)
|
request.Header.Set(model.XAuthTokenKey, account.Token)
|
||||||
|
response, err = http.DefaultTransport.RoundTrip(request)
|
||||||
|
|
||||||
|
response.Header.Add("Set-Cookie", cookie.String())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
request.Header.Set(model.XAuthTokenKey, model.GetBasicAuthAccount("").Token)
|
request.Header.Set(model.XAuthTokenKey, model.GetBasicAuthAccount("").Token)
|
||||||
|
response, err = http.DefaultTransport.RoundTrip(request)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err = http.DefaultTransport.RoundTrip(request)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue