Feature: add authorization for API
This commit is contained in:
parent
454a3a9c7d
commit
27a17c899b
2 changed files with 41 additions and 4 deletions
|
@ -8,7 +8,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapters/outbound"
|
adapters "github.com/Dreamacro/clash/adapters/outbound"
|
||||||
"github.com/Dreamacro/clash/common/observable"
|
"github.com/Dreamacro/clash/common/observable"
|
||||||
"github.com/Dreamacro/clash/common/structure"
|
"github.com/Dreamacro/clash/common/structure"
|
||||||
C "github.com/Dreamacro/clash/constant"
|
C "github.com/Dreamacro/clash/constant"
|
||||||
|
@ -50,6 +50,7 @@ type RawConfig struct {
|
||||||
Mode string `yaml:"mode"`
|
Mode string `yaml:"mode"`
|
||||||
LogLevel string `yaml:"log-level"`
|
LogLevel string `yaml:"log-level"`
|
||||||
ExternalController string `yaml:"external-controller"`
|
ExternalController string `yaml:"external-controller"`
|
||||||
|
Secret string `yaml:"secret"`
|
||||||
|
|
||||||
Proxy []map[string]interface{} `yaml:"Proxy"`
|
Proxy []map[string]interface{} `yaml:"Proxy"`
|
||||||
ProxyGroup []map[string]interface{} `yaml:"Proxy Group"`
|
ProxyGroup []map[string]interface{} `yaml:"Proxy Group"`
|
||||||
|
@ -190,6 +191,7 @@ func (c *Config) parseGeneral(cfg *RawConfig) error {
|
||||||
|
|
||||||
if restAddr := cfg.ExternalController; restAddr != "" {
|
if restAddr := cfg.ExternalController; restAddr != "" {
|
||||||
c.event <- &Event{Type: "external-controller", Payload: restAddr}
|
c.event <- &Event{Type: "external-controller", Payload: restAddr}
|
||||||
|
c.event <- &Event{Type: "secret", Payload: cfg.Secret}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.UpdateGeneral(*c.general)
|
c.UpdateGeneral(*c.general)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package hub
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/config"
|
"github.com/Dreamacro/clash/config"
|
||||||
|
@ -15,6 +16,8 @@ import (
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var secret = ""
|
||||||
|
|
||||||
type Traffic struct {
|
type Traffic struct {
|
||||||
Up int64 `json:"up"`
|
Up int64 `json:"up"`
|
||||||
Down int64 `json:"down"`
|
Down int64 `json:"down"`
|
||||||
|
@ -24,11 +27,19 @@ func newHub(signal chan struct{}) {
|
||||||
var addr string
|
var addr string
|
||||||
ch := config.Instance().Subscribe()
|
ch := config.Instance().Subscribe()
|
||||||
signal <- struct{}{}
|
signal <- struct{}{}
|
||||||
|
count := 0
|
||||||
for {
|
for {
|
||||||
elm := <-ch
|
elm := <-ch
|
||||||
event := elm.(*config.Event)
|
event := elm.(*config.Event)
|
||||||
if event.Type == "external-controller" {
|
switch event.Type {
|
||||||
|
case "external-controller":
|
||||||
addr = event.Payload.(string)
|
addr = event.Payload.(string)
|
||||||
|
count++
|
||||||
|
case "secret":
|
||||||
|
secret = event.Payload.(string)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if count == 2 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,11 +49,11 @@ func newHub(signal chan struct{}) {
|
||||||
cors := cors.New(cors.Options{
|
cors := cors.New(cors.Options{
|
||||||
AllowedOrigins: []string{"*"},
|
AllowedOrigins: []string{"*"},
|
||||||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||||
AllowedHeaders: []string{"Content-Type"},
|
AllowedHeaders: []string{"Content-Type", "Authorization"},
|
||||||
MaxAge: 300,
|
MaxAge: 300,
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Use(cors.Handler)
|
r.Use(cors.Handler, authentication)
|
||||||
|
|
||||||
r.With(jsonContentType).Get("/traffic", traffic)
|
r.With(jsonContentType).Get("/traffic", traffic)
|
||||||
r.With(jsonContentType).Get("/logs", getLogs)
|
r.With(jsonContentType).Get("/logs", getLogs)
|
||||||
|
@ -65,6 +76,30 @@ func jsonContentType(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(fn)
|
return http.HandlerFunc(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func authentication(next http.Handler) http.Handler {
|
||||||
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
header := r.Header.Get("Authorization")
|
||||||
|
text := strings.SplitN(header, " ", 2)
|
||||||
|
|
||||||
|
if secret == "" {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hasUnvalidHeader := text[0] != "Bearer"
|
||||||
|
hasUnvalidSecret := len(text) == 2 && text[1] != secret
|
||||||
|
if hasUnvalidHeader || hasUnvalidSecret {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
render.JSON(w, r, Error{
|
||||||
|
Error: "Authentication failed",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
return http.HandlerFunc(fn)
|
||||||
|
}
|
||||||
|
|
||||||
func traffic(w http.ResponseWriter, r *http.Request) {
|
func traffic(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue