From 05b8d55d0ec9cc546545dcf63efa9bceadca72e2 Mon Sep 17 00:00:00 2001 From: Tulyaganov Shuhrat Date: Tue, 2 May 2023 11:02:06 +0300 Subject: [PATCH] add jwt authentication, and fix cours for next js --- configs/restServer/config.yaml | 2 +- go.mod | 3 + go.sum | 6 ++ internal/bd/bd.go | 11 +++ internal/bd/model/user.go | 10 +- internal/bd/tokenrepo.go | 62 +++++++++++++ internal/bd/userrepo.go | 20 +++- internal/restserver/filmhandle.go | 28 ++---- internal/restserver/middleware.go | 38 ++++++++ internal/restserver/restserver.go | 10 +- internal/restserver/siriesheandle.go | 30 ++---- internal/restserver/userHendle.go | 60 +++++++++++- internal/restserver/usermidleware.go | 134 +++++++++++++++++++++++++++ migrates/20221213130458_films.up.sql | 36 ++++++- pkg/utils/jwt/jwtCheck.go | 24 +++++ pkg/utils/jwt/jwtGenerate.go | 48 ++++++++++ pkg/utils/password/password.go | 13 ++- 17 files changed, 471 insertions(+), 64 deletions(-) create mode 100644 internal/bd/tokenrepo.go create mode 100644 internal/restserver/middleware.go create mode 100644 pkg/utils/jwt/jwtCheck.go diff --git a/configs/restServer/config.yaml b/configs/restServer/config.yaml index 425f311..0cfe84a 100644 --- a/configs/restServer/config.yaml +++ b/configs/restServer/config.yaml @@ -7,4 +7,4 @@ log_level: "debug" # db DB: - baseurlbd: "host=localhost:7000 user=films password=5429593sS dbname=postgres sslmode=disable" \ No newline at end of file + baseurlbd: "" \ No newline at end of file diff --git a/go.mod b/go.mod index 14854e4..1fca7db 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,9 @@ go 1.19 require github.com/sirupsen/logrus v1.9.0 require ( + github.com/felixge/httpsnoop v1.0.1 // indirect github.com/golang-jwt/jwt/v5 v5.0.0-rc.2 // indirect + github.com/gorilla/handlers v1.5.1 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.14.0 // indirect github.com/jackc/pgio v1.0.0 // indirect @@ -15,6 +17,7 @@ require ( github.com/jackc/pgtype v1.14.0 // indirect github.com/jackc/puddle v1.3.0 // indirect github.com/jackc/puddle/v2 v2.2.0 // indirect + github.com/rs/cors v1.9.0 // indirect golang.org/x/sync v0.1.0 // indirect ) diff --git a/go.sum b/go.sum index 6c79231..1bdedba 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -14,6 +16,8 @@ github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx github.com/golang-jwt/jwt/v5 v5.0.0-rc.2 h1:hXPcSazn8wKOfSb9y2m1bdgUMlDxVDarxh3lJVbC6JE= github.com/golang-jwt/jwt/v5 v5.0.0-rc.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= @@ -101,6 +105,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= +github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= diff --git a/internal/bd/bd.go b/internal/bd/bd.go index 9efde3b..bda825d 100644 --- a/internal/bd/bd.go +++ b/internal/bd/bd.go @@ -13,6 +13,7 @@ type Bd struct { filmsrepo *Filmsrepo siriesrepo *Siriesrepo userrepo *Userrepo + tokenrepo *Tokenrepo } func New(config *ConfigBD) *Bd { @@ -68,3 +69,13 @@ func (b *Bd) User() *Userrepo { } return b.userrepo } + +func (b *Bd) Token() *Tokenrepo { + if b.tokenrepo != nil { + return b.tokenrepo + } + b.tokenrepo = &Tokenrepo{ + db: *b, + } + return b.tokenrepo +} diff --git a/internal/bd/model/user.go b/internal/bd/model/user.go index 3caa0b1..db8231f 100644 --- a/internal/bd/model/user.go +++ b/internal/bd/model/user.go @@ -5,6 +5,14 @@ type User struct { Login string `json:"login"` Email string `json:"email"` Password string `json:"password"` - Avatar_Url string + Avatar_Url *string + AccesToken string PermisionLVL int } + +type UserData struct { + Login string + Email string + PermisionLVL int + AccesToken string +} diff --git a/internal/bd/tokenrepo.go b/internal/bd/tokenrepo.go new file mode 100644 index 0000000..1f69072 --- /dev/null +++ b/internal/bd/tokenrepo.go @@ -0,0 +1,62 @@ +package bd + +import ( + "context" + "fmt" +) + +type Tokenrepo struct { + db Bd +} + +func (t *Tokenrepo) Create(login string, refreshToken string) error { + sql := fmt.Sprint("INSERT INTO tokens (login, refreshtoken) VALUES($1, $2)") + _, err := t.db.db.Exec(context.Background(), sql, login, refreshToken) + if err != nil { + return err + } + return nil +} + +func (t *Tokenrepo) FindByLogin(login string) (*string, error) { + var refreshToken string + sql := fmt.Sprint("Select refreshtoken from tokens WHERE login = $1") + rows, err := t.db.db.Query(context.Background(), sql, login) + if err != nil { + return nil, err + } + for rows.Next() { + err = rows.Scan(&refreshToken) + if err != nil { + return nil, err + } + } + + return &refreshToken, nil + +} + +func (t *Tokenrepo) FindByToken(token string) (*string, error) { + var login string + sql := fmt.Sprint("Select login from tokens WHERE refreshtoken = $1") + rows, err := t.db.db.Query(context.Background(), sql, token) + if err != nil { + return nil, err + } + for rows.Next() { + err = rows.Scan(&login) + if err != nil { + return nil, err + } + } + return &login, nil +} + +func (t *Tokenrepo) DeleteByLogin(login string) error { + sql := fmt.Sprint("DELETE FROM tokens WHERE login = $1") + _, err := t.db.db.Exec(context.Background(), sql, login) + if err != nil { + return err + } + return err +} diff --git a/internal/bd/userrepo.go b/internal/bd/userrepo.go index ac04cbb..a44c183 100644 --- a/internal/bd/userrepo.go +++ b/internal/bd/userrepo.go @@ -12,8 +12,8 @@ type Userrepo struct { } func (u *Userrepo) Create(user *model.User) error { - _, err := u.db.db.Exec(context.Background(), - "INSERT INTO users (login, email, password, permisionlvl) VALUES($1, $2, $3, $4)", user.Login, user.Email, user.Password, user.PermisionLVL) + sql := fmt.Sprint("INSERT INTO users (login, email, password, permisionlvl) VALUES($1, $2, $3, $4)") + _, err := u.db.db.Exec(context.Background(), sql, user.Login, user.Email, user.Password, user.PermisionLVL) if err != nil { return err } @@ -60,6 +60,18 @@ func (u *Userrepo) FindByEmail(email string) (*model.User, error) { } -func (u *Userrepo) FindByAll() (*model.User, error) { - return nil, nil +func (u *Userrepo) FindByLoginPas(login string) (*model.User, error) { + var user model.User + rows, err := u.db.db.Query(context.Background(), "SELECT login, password, permisionlvl, email FROM users WHERE login = $1", login) + if err != nil { + return nil, err + } + for rows.Next() { + err := rows.Scan(&user.Login, &user.Password, &user.PermisionLVL, &user.Email) + if err != nil { + return nil, err + } + } + + return &user, nil } diff --git a/internal/restserver/filmhandle.go b/internal/restserver/filmhandle.go index dfa164c..2946a42 100644 --- a/internal/restserver/filmhandle.go +++ b/internal/restserver/filmhandle.go @@ -13,12 +13,12 @@ import ( func (r *RestServer) configureRouterFilms() { r.router.HandleFunc("/api/hello", r.HandleHello()).Methods("GET") - r.router.HandleFunc("/api/films", r.HendleFindAll()).Methods("GET") - r.router.HandleFunc("/api/films/{id:[0-9]+}", r.HendleFindID()).Methods("GET") - r.router.HandleFunc("/api/films/{name}", r.HeandleFilmsFindName()).Methods("GET") - r.router.HandleFunc("/api/films/genres/{name}", r.HeandleFilmsSortGenres()).Methods("GET") - r.router.HandleFunc("/api/films/page/{page:[0-9]+}", r.HendlePagination()).Methods("GET") - r.router.HandleFunc("/api/films/last/", r.HeadleGetLastItem()).Methods("GET") + r.router.HandleFunc("/api/films", r.checkJwtAccess(r.HendleFindAll())).Methods("GET") + r.router.HandleFunc("/api/films/{id:[0-9]+}", r.checkJwtAccess(r.HendleFindID())).Methods("GET") + r.router.HandleFunc("/api/films/{name}", r.checkJwtAccess(r.HeandleFilmsFindName())).Methods("GET") + r.router.HandleFunc("/api/films/genres/{name}", r.checkJwtAccess(r.HeandleFilmsSortGenres())).Methods("GET") + r.router.HandleFunc("/api/films/page/{page:[0-9]+}", r.checkJwtAccess(r.HendlePagination())).Methods("GET") + r.router.HandleFunc("/api/films/last/", r.checkJwtAccess(r.HeadleGetLastItem())).Methods("GET") } func (r *RestServer) HandleHello() http.HandlerFunc { @@ -64,10 +64,6 @@ func (r *RestServer) HendleFindAll() http.HandlerFunc { func (r *RestServer) HeandleFilmsFindName() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - w.Header().Set("Content-Type", "application/json") name := mux.Vars(res)["name"] films, err := r.db.Films().FindByName(name) if err != nil { @@ -88,9 +84,6 @@ func (r *RestServer) HeandleFilmsFindName() http.HandlerFunc { func (r *RestServer) HeandleFilmsSortGenres() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - w.Header().Set("Content-Type", "application/json") name := mux.Vars(res)["name"] films, err := r.db.Films().SortByGanres(name) if err != nil { @@ -106,9 +99,6 @@ func (r *RestServer) HeandleFilmsSortGenres() http.HandlerFunc { func (r *RestServer) HendleFindID() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - w.Header().Set("Content-Type", "application/json") id := mux.Vars(res)["id"] fmt.Println(mux.Vars(res)) film, err := r.db.Films().FindById(id) @@ -126,9 +116,6 @@ func (r *RestServer) HendleFindID() http.HandlerFunc { func (r *RestServer) HendlePagination() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - w.Header().Set("Content-Type", "application/json") id := mux.Vars(res)["page"] films, err := r.db.Films().Pagination(id) if err != nil { @@ -145,9 +132,6 @@ func (r *RestServer) HendlePagination() http.HandlerFunc { func (r *RestServer) HeadleGetLastItem() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - w.Header().Set("Content-Type", "application/json") films, err := r.db.Films().LastItem() if err != nil { r.logger.Errorln(err) diff --git a/internal/restserver/middleware.go b/internal/restserver/middleware.go new file mode 100644 index 0000000..fabe691 --- /dev/null +++ b/internal/restserver/middleware.go @@ -0,0 +1,38 @@ +package restserver + +import ( + "io" + "net/http" + "os" + "strings" + + "git.ukamnya.ru/stulyaganov/RestApiv2/pkg/utils/jwt" +) + +func (r *RestServer) checkJwtAccess(next http.HandlerFunc) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, res *http.Request) { + w.Header().Set("Content-Type", "application/json") + str := res.Header.Values("Authorization") + if str == nil { + w.WriteHeader(http.StatusUnauthorized) + io.WriteString(w, `{"data":"Ошибка Авторизации"}`) + return + } + + token := strings.Split(str[0], " ") + chek, err := jwt.ValidateToken(token[1], []byte(os.Getenv("JWT_SECRET_KEY_ACCESS"))) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + io.WriteString(w, `{"data":"Ошибка Авторизации"}`) + r.logger.Error(err) + return + } + if chek { + next(w, res) + } else { + w.WriteHeader(http.StatusUnauthorized) + io.WriteString(w, `{"data":"Ошибка Авторизации"}`) + } + + }) +} diff --git a/internal/restserver/restserver.go b/internal/restserver/restserver.go index a56f41a..98917d8 100644 --- a/internal/restserver/restserver.go +++ b/internal/restserver/restserver.go @@ -5,6 +5,7 @@ import ( "git.ukamnya.ru/stulyaganov/RestApiv2/internal/bd" "github.com/gorilla/mux" + "github.com/rs/cors" "github.com/sirupsen/logrus" ) @@ -37,14 +38,19 @@ func (r *RestServer) Start() error { if err := r.configurebd(); err != nil { return err } - + corsHendler := cors.New(cors.Options{ + AllowedOrigins: []string{"http://localhost:3000"}, + AllowedHeaders: []string{"Content-Type", "Authorization", "Set-Cookie"}, + AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodOptions, http.MethodDelete, http.MethodPut}, + AllowCredentials: true, + }).Handler(r.router) r.configureRouterFilms() r.configureRouterSiries() r.configureRouterUser() r.logger.Info("Starting Server") r.logger.Info("Listen server on ", r.config.BindPort, " Port") - return http.ListenAndServe(r.config.BindPort, r.router) + return http.ListenAndServe(r.config.BindPort, corsHendler) } func (r *RestServer) configureLogger() error { diff --git a/internal/restserver/siriesheandle.go b/internal/restserver/siriesheandle.go index 08bc207..c1c8682 100644 --- a/internal/restserver/siriesheandle.go +++ b/internal/restserver/siriesheandle.go @@ -12,20 +12,17 @@ import ( ) func (r *RestServer) configureRouterSiries() { - r.router.HandleFunc("/api/siries", r.HendleFindAllSiries()).Methods("GET") - r.router.HandleFunc("/api/siries/{id:[0-9]+}", r.HendleFindIDSiries()).Methods("GET") - r.router.HandleFunc("/api/siries/{name}", r.HeandleSiriesFindName()).Methods("GET") - r.router.HandleFunc("/api/siries/genres/{name}", r.HeandleSiriesSortGenres()).Methods("GET") - r.router.HandleFunc("/api/siries/page/{page:[0-9]+}", r.HendlePaginationSiries()).Methods("GET") - r.router.HandleFunc("/api/siries/lastS/", r.HeadleGetLastItemSiries()).Methods("GET") + r.router.HandleFunc("/api/siries", r.checkJwtAccess(r.HendleFindAllSiries())).Methods("GET") + r.router.HandleFunc("/api/siries/{id:[0-9]+}", r.checkJwtAccess(r.HendleFindIDSiries())).Methods("GET") + r.router.HandleFunc("/api/siries/{name}", r.checkJwtAccess(r.HeandleSiriesFindName())).Methods("GET") + r.router.HandleFunc("/api/siries/genres/{name}", r.checkJwtAccess(r.HeandleSiriesSortGenres())).Methods("GET") + r.router.HandleFunc("/api/siries/page/{page:[0-9]+}", r.checkJwtAccess(r.HendlePaginationSiries())).Methods("GET") + r.router.HandleFunc("/api/siries/lastS/", r.checkJwtAccess(r.HeadleGetLastItemSiries())).Methods("GET") } func (r *RestServer) HandleHelloSiries() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - w.Header().Set("Content-Type", "application/json") id := res.URL.Query().Get("id") fmt.Println(id) cout, err := r.db.Siries().GetCountFilms() @@ -63,9 +60,6 @@ func (r *RestServer) HendleFindAllSiries() http.HandlerFunc { func (r *RestServer) HeandleSiriesFindName() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - w.Header().Set("Content-Type", "application/json") name := mux.Vars(res)["name"] siries, err := r.db.Siries().FindByName(name) if err != nil { @@ -86,9 +80,6 @@ func (r *RestServer) HeandleSiriesFindName() http.HandlerFunc { func (r *RestServer) HeandleSiriesSortGenres() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - w.Header().Set("Content-Type", "application/json") name := mux.Vars(res)["name"] siries, err := r.db.Siries().SortByGanres(name) if err != nil { @@ -104,9 +95,6 @@ func (r *RestServer) HeandleSiriesSortGenres() http.HandlerFunc { func (r *RestServer) HendleFindIDSiries() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - w.Header().Set("Content-Type", "application/json") id := mux.Vars(res)["id"] fmt.Println(mux.Vars(res)) siries, err := r.db.Siries().FindById(id) @@ -123,9 +111,6 @@ func (r *RestServer) HendleFindIDSiries() http.HandlerFunc { func (r *RestServer) HendlePaginationSiries() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - w.Header().Set("Content-Type", "application/json") id := mux.Vars(res)["page"] siries, err := r.db.Siries().Pagination(id) if err != nil { @@ -142,9 +127,6 @@ func (r *RestServer) HendlePaginationSiries() http.HandlerFunc { func (r *RestServer) HeadleGetLastItemSiries() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") - w.Header().Set("Access-Control-Allow-Headers", "Content-Type") - w.Header().Set("Content-Type", "application/json") siries, err := r.db.Siries().LastItem() if err != nil { r.logger.Errorln(err) diff --git a/internal/restserver/userHendle.go b/internal/restserver/userHendle.go index 0c34c2e..3d246df 100644 --- a/internal/restserver/userHendle.go +++ b/internal/restserver/userHendle.go @@ -1,6 +1,7 @@ package restserver import ( + "encoding/json" "fmt" "io" "net/http" @@ -11,8 +12,9 @@ import ( func (r *RestServer) configureRouterUser() { r.router.HandleFunc("/api/register", r.chekUserRegistr(r.HandleFuncRegUser())).Methods("POST") - r.router.HandleFunc("/api/login", r.HandleFuncLoginUser()).Methods("POST") - r.router.HandleFunc("/api/logout", r.HandleFuncLogOutUser()).Methods("POST") + r.router.HandleFunc("/api/login", r.chekUserLogin(r.HandleFuncLoginUser())).Methods("POST") + r.router.HandleFunc("/api/logout", r.chekUserLogout(r.HandleFuncLogOutUser())).Methods("GET") + r.router.HandleFunc("/api/refresh", r.checkUserRefresh(r.HandleFuncRefresh())).Methods("GET") } func (r *RestServer) HandleFuncRegUser() http.HandlerFunc { @@ -29,7 +31,27 @@ func (r *RestServer) HandleFuncRegUser() http.HandlerFunc { if err != nil { r.logger.Error(err) } else { - io.WriteString(w, `{"data":"Пользователь создан"}`) + // Reftoken, err := r.db.Token().FindByLogin(users.Login) + // if err != nil { + // fmt.Println(err) + // } + // if Reftoken != nil { + // users.RefreshToken = *Reftoken + // } else { + // r.db.Token().Create(users.Login, users.RefreshToken) + // } + + // data := &model.UserData{ + // Email: users.Email, + // PermisionLVL: users.PermisionLVL, + // AccesToken: users.AccesToken, + // RefreshToken: users.RefreshToken, + // } + // err = json.NewEncoder(w).Encode(data) + // if err != nil { + // r.logger.Errorln(err) + // } + io.WriteString(w, `{"data":"Пользователь успешно зарегистрирован"}`) } } @@ -38,12 +60,40 @@ func (r *RestServer) HandleFuncRegUser() http.HandlerFunc { func (r *RestServer) HandleFuncLoginUser() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - fmt.Println("login") + users := res.Context().Value(ContextKeyUser).(*model.User) + data := &model.UserData{ + Login: users.Login, + Email: users.Email, + PermisionLVL: users.PermisionLVL, + AccesToken: users.AccesToken, + } + err := json.NewEncoder(w).Encode(data) + if err != nil { + r.logger.Errorln(err) + } + } } func (r *RestServer) HandleFuncLogOutUser() http.HandlerFunc { return func(w http.ResponseWriter, res *http.Request) { - fmt.Println("login") + fmt.Println("Logout") + } +} + +func (r *RestServer) HandleFuncRefresh() http.HandlerFunc { + return func(w http.ResponseWriter, res *http.Request) { + users := res.Context().Value(ContextKeyUser).(*model.User) + data := &model.UserData{ + Login: users.Login, + Email: users.Email, + PermisionLVL: users.PermisionLVL, + AccesToken: users.AccesToken, + } + err := json.NewEncoder(w).Encode(data) + if err != nil { + r.logger.Errorln(err) + } + } } diff --git a/internal/restserver/usermidleware.go b/internal/restserver/usermidleware.go index bdb450f..f2019fe 100644 --- a/internal/restserver/usermidleware.go +++ b/internal/restserver/usermidleware.go @@ -6,8 +6,12 @@ import ( "fmt" "io" "net/http" + "os" + "time" "git.ukamnya.ru/stulyaganov/RestApiv2/internal/bd/model" + "git.ukamnya.ru/stulyaganov/RestApiv2/pkg/utils/jwt" + "git.ukamnya.ru/stulyaganov/RestApiv2/pkg/utils/password" "git.ukamnya.ru/stulyaganov/RestApiv2/pkg/utils/validator" ) @@ -21,6 +25,9 @@ func (r *RestServer) chekUserRegistr(next http.HandlerFunc) http.HandlerFunc { err := json.NewDecoder(res.Body).Decode(users) if err != nil { r.logger.Error(err) + w.WriteHeader(http.StatusBadRequest) + io.WriteString(w, `{"data":"Что-то пошло не так"}`) + return } user, err := r.db.User().FindByLogin(users.Login) if err != nil { @@ -44,6 +51,133 @@ func (r *RestServer) chekUserRegistr(next http.HandlerFunc) http.HandlerFunc { io.WriteString(w, fmt.Sprintf(`{"data":"%s"}`, err)) return } + users.PermisionLVL = defaultPermLvl + // jwtToken, _ := jwt.GenerateTokens(*users) + + // cookie := http.Cookie{ + // Name: "refreshToken", + // Value: jwtToken.RefreshToken, + // Expires: time.Now().Add(time.Hour * 24 * 360), + // HttpOnly: true, + // } + // http.SetCookie(w, &cookie) + // users.AccesToken = jwtToken.AccesToken + // users.RefreshToken = jwtToken.RefreshToken next(w, res.WithContext(context.WithValue(res.Context(), ContextKeyUser, users))) } } + +func (r *RestServer) chekUserLogin(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, res *http.Request) { + users := &model.User{} + err := json.NewDecoder(res.Body).Decode(users) + if err != nil { + r.logger.Error(err) + w.WriteHeader(http.StatusUnauthorized) + io.WriteString(w, `{"data":"Что-то пошло не так"}`) + return + } + user, err := r.db.User().FindByLoginPas(users.Login) + if err != nil { + r.logger.Error(err) + w.WriteHeader(http.StatusUnauthorized) + io.WriteString(w, `{"data":"Что-то пошло не так"}`) + return + } + if user.Login == "" { + w.WriteHeader(http.StatusUnauthorized) + io.WriteString(w, `{"data":"Неверный логин или пароль"}`) + return + } + checkPass := password.CheckValid(users.Password, user.Password) + if !checkPass { + w.WriteHeader(http.StatusUnauthorized) + io.WriteString(w, `{"data":"Неверный логин или пароль"}`) + return + } + jwtToken, _ := jwt.GenerateTokens(*user) + Reftoken, err := r.db.Token().FindByLogin(users.Login) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + io.WriteString(w, `{"data":"Что-то пошло не так "}`) + return + } + if *Reftoken != "" { + jwtToken.RefreshToken = *Reftoken + } else { + r.db.Token().Create(users.Login, jwtToken.RefreshToken) + } + cookie := http.Cookie{ + Name: "refreshToken", + Value: jwtToken.RefreshToken, + Path: "/", + SameSite: http.SameSiteDefaultMode, + Expires: time.Now().Add(time.Hour * 24 * 360), + HttpOnly: true, + } + http.SetCookie(w, &cookie) + users.AccesToken = jwtToken.AccesToken + users.PermisionLVL = user.PermisionLVL + users.Email = user.Email + next(w, res.WithContext(context.WithValue(res.Context(), ContextKeyUser, users))) + } +} + +func (r *RestServer) chekUserLogout(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, res *http.Request) { + checkCookie, err := res.Cookie("refreshToken") + if err != nil { + r.logger.Error(err) + } else { + if checkCookie.Value != "" { + login, _ := r.db.Token().FindByToken(checkCookie.Value) + if *login != "" { + r.db.Token().DeleteByLogin(*login) + } + cookie := http.Cookie{ + Name: "refreshToken", + Value: "", + MaxAge: -1, + Path: "/", + HttpOnly: true, + } + http.SetCookie(w, &cookie) + } + } + + next(w, res) + } +} + +func (r *RestServer) checkUserRefresh(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, res *http.Request) { + users := &model.User{} + checkCookie, err := res.Cookie("refreshToken") + if err != nil { + r.logger.Error(err) + w.WriteHeader(http.StatusUnauthorized) + return + } + chekToken, err := jwt.ValidateToken(checkCookie.Value, []byte(os.Getenv("JWT_SECRET_KEY_REFRESH"))) + if err != nil { + r.logger.Error(err) + w.WriteHeader(http.StatusUnauthorized) + return + } + login, err := r.db.Token().FindByToken(checkCookie.Value) + if err != nil { + r.logger.Error(err) + w.WriteHeader(http.StatusUnauthorized) + return + } + if *login != "" && chekToken { + userdb, _ := r.db.User().FindByLoginPas(*login) + jwtToken, _ := jwt.GenerateTokens(*userdb) + users.Login = userdb.Login + users.AccesToken = jwtToken.AccesToken + users.PermisionLVL = userdb.PermisionLVL + users.Email = userdb.Email + next(w, res.WithContext(context.WithValue(res.Context(), ContextKeyUser, users))) + } + } +} diff --git a/migrates/20221213130458_films.up.sql b/migrates/20221213130458_films.up.sql index 96a8197..b9f65a9 100644 --- a/migrates/20221213130458_films.up.sql +++ b/migrates/20221213130458_films.up.sql @@ -16,11 +16,15 @@ CREATE TABLE films ( iframe_src VARCHAR(2000), ratingImdbVoteCount INTEGER, ratingKinopoiskVoteCount INTEGER, - created VARCHAR(1500), + created VARCHAR(1000), + content_type VARCHAR(25), + ratingAgeLimits VARCHAR(25), media JSON, PRIMARY KEY(id) ); + + CREATE TABLE users ( id SERIAL, login TEXT not NULL, @@ -34,8 +38,32 @@ CREATE TABLE users ( PRIMARY KEY(id) ); - +CREATE TABLE Siries ( + id INTEGER NOT NULL, + ru_title VARCHAR(256), + orig_title VARCHAR(255), + imdb_id VARCHAR(255), + kinopoisk_id INTEGER, + posterUrl VARCHAR(1500), + posterUrlPreview VARCHAR(1500), + countries JSON, + genres JSON, + year INTEGER, + description VARCHAR(20000), + ratingKinopoisk INTEGER, + ratingImdb INTEGER, + iframe_src VARCHAR(2000), + ratingImdbVoteCount INTEGER, + ratingKinopoiskVoteCount INTEGER, + created VARCHAR(1000), + content_type VARCHAR(25), + ratingAgeLimits VARCHAR(25), + translation JSON, + episodes JSON, + PRIMARY KEY(id) +); CREATE TABLE tokens ( - userID INTEGER NOT NULL, - refreshToken TEXT NOT NULL + login TEXT NOT NULL, + refreshToken TEXT NOT NULL, + PRIMARY KEY(login) ); \ No newline at end of file diff --git a/pkg/utils/jwt/jwtCheck.go b/pkg/utils/jwt/jwtCheck.go new file mode 100644 index 0000000..7763a0b --- /dev/null +++ b/pkg/utils/jwt/jwtCheck.go @@ -0,0 +1,24 @@ +package jwt + +import ( + "fmt" + + "github.com/golang-jwt/jwt/v5" +) + +func ValidateToken(tokenString string, secretKey []byte) (bool, error) { + token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return secretKey, nil + }) + if err != nil { + return false, err + } + if _, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + return true, nil + } else { + return false, nil + } +} diff --git a/pkg/utils/jwt/jwtGenerate.go b/pkg/utils/jwt/jwtGenerate.go index e69de29..d8be30e 100644 --- a/pkg/utils/jwt/jwtGenerate.go +++ b/pkg/utils/jwt/jwtGenerate.go @@ -0,0 +1,48 @@ +package jwt + +import ( + "os" + "time" + + "git.ukamnya.ru/stulyaganov/RestApiv2/internal/bd/model" + "github.com/golang-jwt/jwt/v5" +) + +type jwtToken struct { + AccesToken string + RefreshToken string +} + +func GenerateTokens(payload model.User) (*jwtToken, error) { + AccessKey := os.Getenv("JWT_SECRET_KEY_ACCESS") + RefreshKey := os.Getenv("JWT_SECRET_KEY_REFRESH") + claimsAccess := jwt.MapClaims{ + "name": payload.Login, + "permisionlvl": payload.PermisionLVL, + "email": payload.Email, + "exp": time.Now().Add(time.Hour * 24 * 60).Unix(), + } + claimsRefresh := jwt.MapClaims{ + "name": payload.Login, + "permisionlvl": payload.PermisionLVL, + "email": payload.Email, + "exp": time.Now().Add(time.Hour * 24 * 360).Unix(), + } + + tokenAcc := jwt.NewWithClaims(jwt.SigningMethodHS256, claimsAccess) + tokenRef := jwt.NewWithClaims(jwt.SigningMethodHS256, claimsRefresh) + + AccesToken, err := tokenAcc.SignedString([]byte(AccessKey)) + if err != nil { + return nil, err + } + RefreshToken, err := tokenRef.SignedString([]byte(RefreshKey)) + if err != nil { + return nil, err + } + return &jwtToken{ + AccesToken: AccesToken, + RefreshToken: RefreshToken, + }, nil + +} diff --git a/pkg/utils/password/password.go b/pkg/utils/password/password.go index 16cfc8d..93a0208 100644 --- a/pkg/utils/password/password.go +++ b/pkg/utils/password/password.go @@ -1,6 +1,8 @@ package password -import "golang.org/x/crypto/bcrypt" +import ( + "golang.org/x/crypto/bcrypt" +) func HashPassword(password string) (*[]byte, error) { bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14) @@ -9,3 +11,12 @@ func HashPassword(password string) (*[]byte, error) { } return &bytes, err } + +func CheckValid(password string, hash string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + if err != nil { + return false + } + + return true +}