diff --git a/backend/go.mod b/backend/go.mod index 33ccd61..26c4b0e 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -9,6 +9,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gofrs/uuid v4.0.0+incompatible // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/gomodule/redigo v1.8.4 // indirect github.com/googollee/go-socket.io v1.7.0 // indirect github.com/gorilla/context v1.1.2 // indirect diff --git a/backend/go.sum b/backend/go.sum index e10003f..7221f93 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -14,6 +14,8 @@ github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIx github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg= github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/googollee/go-socket.io v1.7.0 h1:ODcQSAvVIPvKozXtUGuJDV3pLwdpBLDs1Uoq/QHIlY8= diff --git a/backend/internal/controllers/download.go b/backend/internal/controllers/download.go index c6adba2..5a19fd4 100644 --- a/backend/internal/controllers/download.go +++ b/backend/internal/controllers/download.go @@ -7,41 +7,37 @@ import ( "backend/middleware" "errors" "fmt" + "time" + "github.com/golang-jwt/jwt/v5" "github.com/labstack/echo/v4" ) +type DownloadShareClaims struct { + ShareId string `json:"share_id"` + jwt.RegisteredClaims +} + func DownloadShare(c echo.Context) error { cc := c.(*middleware.CustomContext) - shareId := cc.Param("id") - password := cc.QueryParam("password") - - if shareId == "" { - return utils.HTTPErrorHandler(c, errors.New("缺少分享ID")) + token := cc.FormValue("token") + if token == "" { + return utils.HTTPErrorHandler(c, errors.New("缺少token")) } - - shareInfo, err := models.GetRedisShareInfo(shareId) + claims := DownloadShareClaims{} + t, err := jwt.ParseWithClaims(token, &claims, func(token *jwt.Token) (interface{}, error) { + return []byte(utils.GetEnv("download_secret")), nil + }) if err != nil { return utils.HTTPErrorHandler(c, err) } - if shareInfo == nil { - return utils.HTTPErrorHandler(c, errors.New("分享不存在")) - } - if shareInfo.Password != "" && shareInfo.Password != password { - return utils.HTTPErrorHandler(c, errors.New("分享密码错误")) + if !t.Valid { + return utils.HTTPErrorHandler(c, errors.New("token格式错误")) } + shareInfo, _ := models.GetRedisShareInfo(claims.ShareId) if shareInfo.Type == models.ShareTypeFile { - fileInfo, err := models.GetRedisFileInfo(shareInfo.Data) - if err != nil { - return utils.HTTPErrorHandler(c, err) - } - if fileInfo == nil { - return utils.HTTPErrorHandler(c, errors.New("分享文件不存在")) - } - if fileInfo.FileType != models.FileTypeUpload { - return utils.HTTPErrorHandler(c, errors.New("分享文件状态错误")) - } + fileInfo, _ := models.GetRedisFileInfo(shareInfo.Data) uploadPath, err := services.GetUploadDirPath() if err != nil { return err @@ -53,3 +49,62 @@ func DownloadShare(c echo.Context) error { "data": shareInfo.Data, }) } + +type VaildateShareProps struct { + ShareId string `json:"share_id"` + Password string `json:"password"` +} + +func VaildateShare(c echo.Context) error { + cc := c.(*middleware.CustomContext) + + r := new(VaildateShareProps) + if err := cc.Bind(r); err != nil { + return utils.HTTPErrorHandler(c, err) + } + + if r.ShareId == "" { + return utils.HTTPErrorHandler(c, errors.New("缺少分享ID")) + } + + shareInfo, err := models.GetRedisShareInfo(r.ShareId) + if err != nil { + return utils.HTTPErrorHandler(c, err) + } + if shareInfo == nil { + return utils.HTTPErrorHandler(c, errors.New("分享不存在")) + } + if shareInfo.Password != "" && shareInfo.Password != r.Password { + return utils.HTTPErrorHandler(c, errors.New("分享密码错误")) + } + token := jwt.NewWithClaims(jwt.SigningMethodHS256, DownloadShareClaims{ + ShareId: r.ShareId, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(60 * time.Minute)), + }, + }) + + // Sign and get the complete encoded token as a string using the secret + downloadToken, err := token.SignedString([]byte(utils.GetEnv("download_secret"))) + if err != nil { + return utils.HTTPErrorHandler(c, err) + } + if shareInfo.Type == models.ShareTypeFile { + fileInfo, err := models.GetRedisFileInfo(shareInfo.Data) + if err != nil { + return utils.HTTPErrorHandler(c, err) + } + if fileInfo == nil { + return utils.HTTPErrorHandler(c, errors.New("分享文件不存在")) + } + if fileInfo.FileType != models.FileTypeUpload { + return utils.HTTPErrorHandler(c, errors.New("分享文件状态错误")) + } + return utils.HTTPSuccessHandler(c, map[string]any{ + "token": downloadToken, + }) + } + return utils.HTTPSuccessHandler(c, map[string]any{ + "token": downloadToken, + }) +} diff --git a/backend/main.go b/backend/main.go index b9f47d7..d564279 100644 --- a/backend/main.go +++ b/backend/main.go @@ -21,7 +21,8 @@ func main() { e.POST("/share", controllers.CreateShareInfo) e.GET("/config", controllers.GetConfig) - e.GET("/download/:id", controllers.DownloadShare) + e.GET("/download", controllers.DownloadShare) + e.POST("/download", controllers.VaildateShare) e.Logger.Fatal(e.Start(":1323")) }