From b3d142503c14408bc8c2afb6e3b2055b7fb7e6b5 Mon Sep 17 00:00:00 2001 From: ZSC714725 Date: Fri, 27 May 2022 15:34:51 +0800 Subject: [PATCH] rtsp server support basic auth --- pkg/logic/config.go | 2 ++ pkg/logic/server_manager.go | 5 +-- pkg/rtsp/pack.go | 11 +++++++ pkg/rtsp/server.go | 15 +++++++-- pkg/rtsp/server_command_session.go | 50 +++++++++++++++++++++++++++++- 5 files changed, 77 insertions(+), 6 deletions(-) diff --git a/pkg/logic/config.go b/pkg/logic/config.go index fb4f739..62dfd46 100644 --- a/pkg/logic/config.go +++ b/pkg/logic/config.go @@ -17,6 +17,7 @@ import ( "github.com/q191201771/lal/pkg/base" "github.com/q191201771/lal/pkg/hls" + "github.com/q191201771/lal/pkg/rtsp" "github.com/q191201771/naza/pkg/nazajson" "github.com/q191201771/naza/pkg/nazalog" ) @@ -87,6 +88,7 @@ type RtspConfig struct { Enable bool `json:"enable"` Addr string `json:"addr"` OutWaitKeyFrameFlag bool `json:"out_wait_key_frame_flag"` + rtsp.RtspServerAuthConfig } type RecordConfig struct { diff --git a/pkg/logic/server_manager.go b/pkg/logic/server_manager.go index 1230fcc..7be4dd0 100644 --- a/pkg/logic/server_manager.go +++ b/pkg/logic/server_manager.go @@ -11,13 +11,14 @@ package logic import ( "flag" "fmt" - "github.com/q191201771/naza/pkg/nazalog" "net/http" "os" "path/filepath" "sync" "time" + "github.com/q191201771/naza/pkg/nazalog" + "github.com/q191201771/naza/pkg/defertaskthread" "github.com/q191201771/lal/pkg/hls" @@ -125,7 +126,7 @@ Doc: %s sm.rtmpServer = rtmp.NewServer(sm.config.RtmpConfig.Addr, sm) } if sm.config.RtspConfig.Enable { - sm.rtspServer = rtsp.NewServer(sm.config.RtspConfig.Addr, sm) + sm.rtspServer = rtsp.NewServer(sm.config.RtspConfig.Addr, sm, sm.config.RtspConfig.RtspServerAuthConfig) } if sm.config.HttpApiConfig.Enable { sm.httpApiServer = NewHttpApiServer(sm.config.HttpApiConfig.Addr, sm) diff --git a/pkg/rtsp/pack.go b/pkg/rtsp/pack.go index 294111d..016ee03 100644 --- a/pkg/rtsp/pack.go +++ b/pkg/rtsp/pack.go @@ -77,6 +77,12 @@ var ResponseTeardownTmpl = "RTSP/1.0 200 OK\r\n" + "CSeq: %s\r\n" + "\r\n" +var ResponseBasicAuthorizedTmpl = "RTSP/1.0 401 Unauthorized\r\n" + + "CSeq: %s\r\n" + + "Date: %s\r\n" + + "WWW-Authenticate: %s realm=\"Lal Server\"\r\n" + + "\r\n" + func PackResponseOptions(cseq string) string { return fmt.Sprintf(ResponseOptionsTmpl, cseq) } @@ -109,6 +115,11 @@ func PackResponseTeardown(cseq string) string { return fmt.Sprintf(ResponseTeardownTmpl, cseq) } +func PackResponseBasicAuthorized(cseq, authMethod string) string { + date := time.Now().Format(time.RFC1123) + return fmt.Sprintf(ResponseBasicAuthorizedTmpl, cseq, date, authMethod) +} + // PackRequest @param body 可以为空 func PackRequest(method, uri string, headers map[string]string, body string) (ret string) { ret = method + " " + uri + " RTSP/1.0\r\n" diff --git a/pkg/rtsp/server.go b/pkg/rtsp/server.go index 1c29317..42b2156 100644 --- a/pkg/rtsp/server.go +++ b/pkg/rtsp/server.go @@ -49,17 +49,26 @@ type IServerObserver interface { OnDelRtspSubSession(session *SubSession) } +type RtspServerAuthConfig struct { + AuthEnable bool `json:"auth_enable"` + AuthMethod int `json:"auth_method"` + UserName string `json:"username"` + PassWord string `json:"password"` +} + type Server struct { addr string observer IServerObserver - ln net.Listener + ln net.Listener + auth RtspServerAuthConfig } -func NewServer(addr string, observer IServerObserver) *Server { +func NewServer(addr string, observer IServerObserver, auth RtspServerAuthConfig) *Server { return &Server{ addr: addr, observer: observer, + auth: auth, } } @@ -116,7 +125,7 @@ func (s *Server) OnDelRtspSubSession(session *SubSession) { // --------------------------------------------------------------------------------------------------------------------- func (s *Server) handleTcpConnect(conn net.Conn) { - session := NewServerCommandSession(s, conn) + session := NewServerCommandSession(s, conn, s.auth) s.observer.OnNewRtspSessionConnect(session) err := session.RunLoop() diff --git a/pkg/rtsp/server_command_session.go b/pkg/rtsp/server_command_session.go index 853afb7..de5e419 100644 --- a/pkg/rtsp/server_command_session.go +++ b/pkg/rtsp/server_command_session.go @@ -10,6 +10,7 @@ package rtsp import ( "bufio" + "encoding/base64" "fmt" "net" "strings" @@ -54,16 +55,18 @@ type ServerCommandSession struct { prevConnStat connection.Stat staleStat *connection.Stat stat base.StatSession + auth RtspServerAuthConfig pubSession *PubSession subSession *SubSession } -func NewServerCommandSession(observer IServerCommandSessionObserver, conn net.Conn) *ServerCommandSession { +func NewServerCommandSession(observer IServerCommandSessionObserver, conn net.Conn, auth RtspServerAuthConfig) *ServerCommandSession { uk := base.GenUkRtspServerCommandSession() s := &ServerCommandSession{ uniqueKey: uk, observer: observer, + auth: auth, conn: connection.New(conn, func(option *connection.Option) { option.ReadBufSize = serverCommandSessionReadBufSize option.WriteChanSize = serverCommandSessionWriteChanSize @@ -251,6 +254,19 @@ func (session *ServerCommandSession) handleAnnounce(requestCtx nazahttp.HttpReqM func (session *ServerCommandSession) handleDescribe(requestCtx nazahttp.HttpReqMsgCtx) error { Log.Infof("[%s] < R DESCRIBE", session.uniqueKey) + if session.auth.AuthEnable { + // 鉴权处理 + authresp, err := session.handleAuthorized(requestCtx) + if err != nil { + return err + } + + if authresp != "" { + _, err := session.conn.Write([]byte(authresp)) + return err + } + } + urlCtx, err := base.ParseRtspUrl(requestCtx.Uri) if err != nil { Log.Errorf("[%s] parse presentation failed. uri=%s", session.uniqueKey, requestCtx.Uri) @@ -273,6 +289,38 @@ func (session *ServerCommandSession) handleDescribe(requestCtx nazahttp.HttpReqM return err } +func (session *ServerCommandSession) handleAuthorized(requestCtx nazahttp.HttpReqMsgCtx) (string, error) { + if requestCtx.Headers.Get(HeaderAuthorization) != "" { + auth_str := requestCtx.Headers.Get(HeaderAuthorization) + if strings.Contains(auth_str, AuthTypeBasic) { + // Basic 鉴权 + auth_base64_client := strings.TrimLeft(auth_str, "Basic ") + + authstr := fmt.Sprintf("%s:%s", session.auth.UserName, session.auth.PassWord) + auth_base64_server := base64.StdEncoding.EncodeToString([]byte(authstr)) + if auth_base64_server == auth_base64_client { + Log.Infof("[%s] Rtsp Basic auth success. uri=%s", session.uniqueKey, requestCtx.Uri) + } else { + err := fmt.Errorf("Rtsp Basic auth failed, auth:%s", auth_base64_client) + return "", err + } + } else { + err := fmt.Errorf("unsupport, auth method:%d", session.auth.AuthMethod) + return "", err + } + } else { + if session.auth.AuthMethod == 0 { + resp := PackResponseBasicAuthorized(requestCtx.Headers.Get(HeaderCSeq), AuthTypeBasic) + return resp, nil + } else { + err := fmt.Errorf("unsupport, auth method:%d", session.auth.AuthMethod) + return "", err + } + } + + return "", nil +} + // 一次SETUP对应一路流(音频或视频) func (session *ServerCommandSession) handleSetup(requestCtx nazahttp.HttpReqMsgCtx) error { Log.Infof("[%s] < R SETUP", session.uniqueKey)