From 149b0619e0cb9efcf4a52be0b0bb994be9ff4f6b Mon Sep 17 00:00:00 2001 From: maksim Date: Sun, 23 Feb 2025 22:13:45 +0400 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D1=80=D0=B5=D0=B3=D0=B8=D1=81=D1=82=D1=80=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8E/=D0=B0=D0=B2=D1=82=D0=BE=D1=80=D0=B8=D0=B7=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go-auth/.gitignore | 2 +- go-auth/README.md | 10 ++- go-auth/controllers/authController.go | 41 +++++----- go-auth/go.mod | 34 +++++++++ go-auth/go.sum | 67 +++++++++++++++++ go-auth/main.go | 2 +- react-auth/src/App.jsx | 2 + .../src/component/page/Authorization.jsx | 67 ++++++++++------- .../src/component/page/Registration.jsx | 74 +++++++++++-------- react-auth/src/component/profile/Profile.jsx | 56 ++++++++++++++ 10 files changed, 279 insertions(+), 76 deletions(-) create mode 100644 go-auth/go.mod create mode 100644 go-auth/go.sum create mode 100644 react-auth/src/component/profile/Profile.jsx diff --git a/go-auth/.gitignore b/go-auth/.gitignore index eaeb163..451fb35 100644 --- a/go-auth/.gitignore +++ b/go-auth/.gitignore @@ -1,2 +1,2 @@ .env -go.mod \ No newline at end of file +.idea \ No newline at end of file diff --git a/go-auth/README.md b/go-auth/README.md index 7ef54cb..0e60c3e 100644 --- a/go-auth/README.md +++ b/go-auth/README.md @@ -1,3 +1,11 @@ # PIbd-42_Kashin_M.I_FinalQualifyingWork -Модуль "Авторизация" \ No newline at end of file +Модуль "Авторизация" + +go mod init main + +go mod tidy + +.env +JWT_SECRET_KEY=mysecretkey123 +DB_URL=postgres://postgres:password_db@localhost:5432/name_db \ No newline at end of file diff --git a/go-auth/controllers/authController.go b/go-auth/controllers/authController.go index 466d6b7..c94d81d 100644 --- a/go-auth/controllers/authController.go +++ b/go-auth/controllers/authController.go @@ -8,7 +8,6 @@ import ( "main/database" "main/models" "os" - "strconv" "time" "github.com/joho/godotenv" @@ -60,44 +59,50 @@ func Register(c *fiber.Ctx) error { return c.JSON(user) } -// Логиним пользователя +// Генерация JWT-токена +func generateToken(user models.User) (string, error) { + claims := jwt.MapClaims{ + "user_id": user.Id, + "exp": time.Now().Add(time.Hour * 24).Unix(), // Токен на 24 часа + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString([]byte(SecretKey)) +} + func Login(c *fiber.Ctx) error { var data map[string]string if err := c.BodyParser(&data); err != nil { return err } + // Найти пользователя по email var user models.User database.DB.Where("email = ?", data["email"]).First(&user) - if user.Id == 0 { - return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"message": "User not found"}) + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid email or password"}) } + // Проверяем пароль if err := bcrypt.CompareHashAndPassword(user.Password, []byte(data["password"])); err != nil { - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Incorrect password"}) + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Invalid email or password"}) } - claims := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{ - Issuer: strconv.Itoa(int(user.Id)), - ExpiresAt: time.Now().Add(time.Hour * 12).Unix(), - }) - - token, err := claims.SignedString([]byte(SecretKey)) + // Генерируем токен + token, err := generateToken(user) if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"message": "Token invalid"}) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"message": "Could not generate token"}) } - cookie := fiber.Cookie{ + // Сохраняем токен в cookie + c.Cookie(&fiber.Cookie{ Name: "jwt", Value: token, - Expires: time.Now().Add(time.Hour * 12), + Expires: time.Now().Add(time.Hour * 24), HTTPOnly: true, - SameSite: "Strict", - } - c.Cookie(&cookie) + }) - return c.JSON(fiber.Map{"message": "Login success"}) + return c.JSON(fiber.Map{"message": "Login successful"}) } // Получаем пользователя по токену diff --git a/go-auth/go.mod b/go-auth/go.mod new file mode 100644 index 0000000..a863feb --- /dev/null +++ b/go-auth/go.mod @@ -0,0 +1,34 @@ +module main + +go 1.24.0 + +require ( + github.com/gofiber/fiber/v2 v2.52.6 + github.com/golang-jwt/jwt v3.2.2+incompatible + github.com/joho/godotenv v1.5.1 + golang.org/x/crypto v0.34.0 + gorm.io/driver/postgres v1.5.11 + gorm.io/gorm v1.25.12 +) + +require ( + github.com/andybalholm/brotli v1.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.5.5 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.51.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect +) diff --git a/go-auth/go.sum b/go-auth/go.sum new file mode 100644 index 0000000..7465adf --- /dev/null +++ b/go-auth/go.sum @@ -0,0 +1,67 @@ +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +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/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI= +github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= +github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +golang.org/x/crypto v0.34.0 h1:+/C6tk6rf/+t5DhUketUbD1aNGqiSX3j15Z6xuIDlBA= +golang.org/x/crypto v0.34.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= +gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= diff --git a/go-auth/main.go b/go-auth/main.go index 30acf05..b63ca5b 100644 --- a/go-auth/main.go +++ b/go-auth/main.go @@ -23,7 +23,7 @@ func main() { app.Use(cors.New(cors.Config{ AllowCredentials: true, - AllowOrigins: "http://localhost:8000", + AllowOrigins: "http://localhost:5173/", })) routes.Setup(app) diff --git a/react-auth/src/App.jsx b/react-auth/src/App.jsx index 1d5ea4d..1e06f57 100644 --- a/react-auth/src/App.jsx +++ b/react-auth/src/App.jsx @@ -2,6 +2,7 @@ import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import Authorization from './component/page/Authorization'; import Registration from './component/page/Registration'; import Main from './component/page/Main'; +import Profile from './component/profile/Profile'; function App() { return ( @@ -10,6 +11,7 @@ function App() { } /> } /> } /> + } /> ); diff --git a/react-auth/src/component/page/Authorization.jsx b/react-auth/src/component/page/Authorization.jsx index 83680ca..f976fac 100644 --- a/react-auth/src/component/page/Authorization.jsx +++ b/react-auth/src/component/page/Authorization.jsx @@ -1,48 +1,63 @@ -import { Link } from 'react-router-dom'; // Импортируем Link - -import React from 'react'; +import { Link, useNavigate } from 'react-router-dom'; +import React, { useState } from 'react'; import { LockOutlined, UserOutlined } from '@ant-design/icons'; -import { Button, Checkbox, Form, Input, Flex } from 'antd'; +import { Button, Checkbox, Form, Input, Flex, message } from 'antd'; +import axios from 'axios'; const Authorization = () => { - const onFinish = (values) => { - console.log('Received values of form: ', values); + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); // Перенаправление после успешного входа + + const onFinish = async (values) => { + setLoading(true); + try { + const response = await axios.post( + 'http://localhost:8000/api/login', + { + email: values.email, // Обновляем поле, так как сервер ожидает email + password: values.password, + }, + { withCredentials: true } // Обязательно, чтобы браузер хранил cookie + ); + + message.success('Login successful!'); + console.log(response.data); + + // Перенаправление на главную страницу + navigate('/'); + } catch (error) { + message.error(error.response?.data?.message || 'Login failed'); + console.error(error); + } finally { + setLoading(false); + } }; return (
- } placeholder="Username" /> + } placeholder="Email" /> + } type="password" placeholder="Password" /> + @@ -53,10 +68,10 @@ const Authorization = () => { - - or Register now! {/* Используем Link для перехода на страницу регистрации */} + or Register now!
diff --git a/react-auth/src/component/page/Registration.jsx b/react-auth/src/component/page/Registration.jsx index 41a4f54..c522be5 100644 --- a/react-auth/src/component/page/Registration.jsx +++ b/react-auth/src/component/page/Registration.jsx @@ -1,48 +1,64 @@ -import { Link } from 'react-router-dom'; // Импортируем Link - -import React from 'react'; -import { LockOutlined, UserOutlined } from '@ant-design/icons'; -import { Button, Checkbox, Form, Input, Flex } from 'antd'; +import { Link } from 'react-router-dom'; +import React, { useState } from 'react'; +import { LockOutlined, UserOutlined, MailOutlined } from '@ant-design/icons'; +import { Button, Checkbox, Form, Input, Flex, message } from 'antd'; +import axios from 'axios'; const Registration = () => { - const onFinish = (values) => { - console.log('Received values of form: ', values); + const [loading, setLoading] = useState(false); // Для отображения загрузки + + const onFinish = async (values) => { + setLoading(true); + try { + const response = await axios.post('http://localhost:8000/api/register', { + name: values.name, + email: values.email, + password: values.password, + }); + + + message.success('Registration successful!'); + console.log(response.data); // Можно обработать ответ, например, переадресовать на login + } catch (error) { + message.error(error.response?.data?.message || 'Registration failed'); + console.error(error); + } finally { + setLoading(false); + } }; return (
+ } placeholder="Name" /> + + + - } placeholder="Username" /> + } placeholder="Email" /> + } type="password" placeholder="Password" /> + @@ -53,10 +69,10 @@ const Registration = () => { - - or Login now! {/* Используем Link для перехода на страницу авторизации */} + or Login now!
diff --git a/react-auth/src/component/profile/Profile.jsx b/react-auth/src/component/profile/Profile.jsx new file mode 100644 index 0000000..9681f70 --- /dev/null +++ b/react-auth/src/component/profile/Profile.jsx @@ -0,0 +1,56 @@ +import React, { useEffect, useState } from "react"; +import axios from "axios"; +import { useNavigate } from "react-router-dom"; +import { Card, Spin, Alert, Button } from "antd"; + +const Profile = () => { + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const navigate = useNavigate(); + + useEffect(() => { + const fetchUserData = async () => { + try { + const response = await axios.get("http://localhost:8000/api/user", { + withCredentials: true, // Отправляем cookie с JWT + }); + } catch (err) { + setError("Вы не авторизованы. Пожалуйста, войдите в систему."); + } finally { + setLoading(false); + } + }; + + fetchUserData(); + }, []); + + if (loading) return ; + if (error) + return ( + navigate("/login")}> + Войти + + } + /> + ); + + return ( +
+ +

Имя: имя

+

Email: почта

+ +
+
+ ); +}; + +export default Profile;