Feature: add authorization for API

This commit is contained in:
Dreamacro 2018-10-06 13:15:02 +08:00
parent ea9cd41197
commit 8fff0968bf
2 changed files with 41 additions and 4 deletions

View file

@ -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)

View file

@ -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)