Skip to content
Snippets Groups Projects
Commit 40482b78 authored by Jaeyong Lee's avatar Jaeyong Lee
Browse files
parents a9884814 8e43232a
No related branches found
No related tags found
No related merge requests found
......@@ -14,6 +14,8 @@
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-session": "^1.17.3",
"memorystore": "^1.6.7",
"moment": "^2.29.4",
"mongoose": "^8.0.1",
"nodemon": "^3.0.1"
......@@ -504,6 +506,32 @@
"node": ">= 0.10.0"
}
},
"node_modules/express-session": {
"version": "1.17.3",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
"integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
"dependencies": {
"cookie": "0.4.2",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.0.2",
"parseurl": "~1.3.3",
"safe-buffer": "5.2.1",
"uid-safe": "~2.1.5"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/express-session/node_modules/cookie": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
......@@ -803,6 +831,53 @@
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
},
"node_modules/memorystore": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/memorystore/-/memorystore-1.6.7.tgz",
"integrity": "sha512-OZnmNY/NDrKohPQ+hxp0muBcBKrzKNtHr55DbqSx9hLsYVNnomSAMRAtI7R64t3gf3ID7tHQA7mG4oL3Hu9hdw==",
"dependencies": {
"debug": "^4.3.0",
"lru-cache": "^4.0.3"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/memorystore/node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/memorystore/node_modules/lru-cache": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
"dependencies": {
"pseudomap": "^1.0.2",
"yallist": "^2.1.2"
}
},
"node_modules/memorystore/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/memorystore/node_modules/yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
......@@ -1087,6 +1162,14 @@
"node": ">= 0.8"
}
},
"node_modules/on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
......@@ -1128,6 +1211,11 @@
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
},
"node_modules/pstree.remy": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
......@@ -1155,6 +1243,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
......@@ -1396,6 +1492,17 @@
"node": ">= 0.6"
}
},
"node_modules/uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"dependencies": {
"random-bytes": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
......
......@@ -17,6 +17,8 @@
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-session": "^1.17.3",
"memorystore": "^1.6.7",
"moment": "^2.29.4",
"mongoose": "^8.0.1",
"nodemon": "^3.0.1"
......
......@@ -4,7 +4,7 @@ import path from 'path'
import process from 'process'
import moment from 'moment';
import cookieParser from 'cookie-parser';
import session from 'express-session';
import db from './db.js'
const app = express();
......@@ -14,6 +14,18 @@ db.connectDB();
app.use(express.static(path.join(process.cwd(), '../frontend/build')));
const maxAge = 1000 * 60 * 1; // 5분(임시)
app.use(session({
secret: '12345',
resave: true,
saveUninitialized: true,
cookie: {
secure: false,
maxAge: maxAge
}
}))
app.use(cookieParser());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
......@@ -37,19 +49,63 @@ function encode_utf8(s) {
return unescape(encodeURIComponent(s));
}
app.post('/login', (req, res) => {
app.post('/login', async (req, res) => {
/*
TODO: 토큰의 무결성 체크
토큰이 이상이 없다면, 로그인/회원가입 로직을 수행 후 jwt 쿠키를 보낸다.
*/
const expires = moment().add('2','m').toDate()
res.cookie('jwt', JSON.stringify(req.body), {expires});
const expires = moment().add('1','m').toDate()
// 정보가 없다면 회원 가입 (강제?)
const user = await db.UserModel.find({ user_id: req.body.email });
if (!user.length) { // 유저가 없다면 회원 가입 후 세션 생성
let userProfilePicture = req.body.picture || null
const userModel = new db.UserModel({
user_id: req.body.email,
nickname: req.body.name,
email: req.body.email,
google: {
id: req.body.sub,
profileUrl: userProfilePicture,
},
});
await userModel.save();
console.log('saved')
}
req.session.sessionid = req.body.name; //세션 생성
res.cookie('name', req.body.name, {expires}); //사용자 이름 쿠키
if(req.session.sessionid){
console.log(req.session.sessionid);
//res.send('세션 o');
}
//통과 못하면 에러를 뱉는다
res.send(req.body.name);
});
app.get("/logout", (req, res) => {
console.log("로그아웃");
if (req.session.sessionid) {
console.log("로그아웃중입니다!");
req.session.destroy((err) => {
if (err) {
console.log("세션 삭제시에 에러가 발생했습니다.");
return;
}
console.log("세션이 삭제됐습니다.");
});
res.clearCookie('name');
res.send(req.body.name);
} else {
console.log("로그인이 안돼있으시네요?");
res.send(req.body.name);
}
});
......
......@@ -2,20 +2,28 @@ import "./App.css";
import {Routes, Route } from 'react-router-dom';
import Header from "./Header.js";
import Main from "./Main.js";
import Login from "./Login.js";
import Search from "./Search.js";
import GoogleLoginButton from "./GoogleLoginButton.js";
import React, { useEffect, useState, useContext} from 'react';
import { UserContext } from './Usercontext.js';
function App() {
const [ myName, setMyName] = useState(null);
return(
<div className="App">
<Header/>
<Routes>
<Route path="/" element={<Main/>}></Route>
<Route path="/search" element={<Search/>}></Route>
{/* <Route path="/postwrite" element={<PostWrite/>}></Route> */}
</Routes>
<GoogleLoginButton/>
{/* <Footer/> */}
<UserContext.Provider value={setMyName}>
<Header user={myName}></Header>
<Routes>
<Route path="/" element={<Main/>}></Route>
<Route path="/login" element={<Login/>}></Route>
<Route path="/search" element={<Search/>}></Route>
{/* <Route path="/postwrite" element={<PostWrite/>}></Route> */}
</Routes>
</UserContext.Provider>
{/* <Footer/> */}
</div>
);
}
......
......@@ -2,6 +2,8 @@ import { GoogleLogin } from "@react-oauth/google";
import { GoogleOAuthProvider } from "@react-oauth/google";
import { useNavigate, Navigate } from "react-router-dom";
import { useCookies } from 'react-cookie'
import React, { useEffect, useState, useContext} from 'react';
import { UserContext } from './Usercontext.js';
// 안써도 자동으로 한국 시간을 불러온다. 명확하게 하기 위해 import
import moment from 'moment';
......@@ -11,37 +13,24 @@ import base64 from 'base-64';
import axios from 'axios';
axios.defaults.withCredentials = true;
/*
*/
const GoogleLoginButton = () => {
const setUserName = useContext(UserContext);
const [cookies, setCookie, removeCookie] = useCookies();
const clientId = '716858812522-rb0pfisq317unkh4so5hvbu16p19kqp8.apps.googleusercontent.com'
const navigate = useNavigate();
const goMain = () => {
navigate("/main");
navigate("/");
}
return (
<>
<GoogleOAuthProvider clientId={clientId}>
<GoogleLogin
onSuccess={(res) => {
/* 발급받은 토큰은 . 을 기준으로 3 개로 나뉜다.
aaaa.bbbb.cccc
[base64]aaaa: 헤더
[base64]bbbb: 페이로드 (실질적인 데이터)
[RS256]cccc: 서명
RS256 : 암호화 알고리즘, JWT 서명할 때 사용한다고 함
*/
// 쿠키 테스트 1분 뒤 만료
const expires = moment().add('1','m').toDate()
setCookie('cookieTest','hello',{expires})
let datas = res.credential.split('.')
const obj = JSON.parse(b64DecodeUnicode(datas[1]));
......@@ -49,11 +38,13 @@ const GoogleLoginButton = () => {
// 토큰을 보내 로그인 로직 처리
// 로그인이 정상적으로 되었다면 쿠키를 등록
let response = requestLogin(obj);
console.log(cookies.jwt)
if (response) {
goMain();
}
let response = requestLogin(obj).then(
(val) => {
setUserName(val)
goMain();
}
);
}}
onFailure={(err) => {
console.log("Login Failed");
......@@ -79,7 +70,6 @@ async function requestLogin(payloadObj) {
method: 'post', // 통신할 방식
data: payloadObj
});
console.log(response)
if (response.status === 200) {
return response.data;
}
......
import {Link} from "react-router-dom";
import logo from './logo.png';
import './Header.css';
import cookie from 'react-cookies';
import React, { useEffect, useState, useContext} from 'react';
import { useNavigate, Navigate } from "react-router-dom";
import { UserContext } from './Usercontext.js';
import axios from 'axios';
axios.defaults.withCredentials = true;
function Header(){
function Welcome(props){
return (
<p>
{props.name?`${props.name}님, 환영합니다`:'로그인하세요.'}<br/>
</p>
)
}
function ButtonLink({link, onclick, children}){
return (
<botton onClick = {()=>{
onclick(link)}}>
{children}
</botton>
)
}
function Header({user}){
//console.log(cookie.load('name'))
const [currentSession, setCurrentSession] = useState(false)
const setUserName = useContext(UserContext);
const navigate = useNavigate();
function checkSession_And_Navigate(link){
if (cookie.load('name')) {
navigate(link);
setCurrentSession(true)
}
else {
setUserName(null)
if (currentSession){
alert('세션이 만료되었습니다. 로그인 후 이용해 주세요.')
}
else {
alert('로그인 후 이용해 주세요.')
}
navigate("/login");
setCurrentSession(false)
}
}
function dont_CheckSession_And_Navigate(link){
if (cookie.load('name')) {
setCurrentSession(true)
}
else {
setUserName(null)
if (currentSession){
alert('세션이 만료되었습니다. 자동으로 로그아웃됩니다.')
}
setCurrentSession(false)
}
navigate(link);
}
function logOut(){
let response = requestLogout();
if (response){
setUserName(null)
alert('로그아웃되었습니다. 메인 화면으로 돌아갑니다..')
setCurrentSession(false)
navigate('/');
}
}
return(
<div className="header">
<Link to='/'><img className="logo_image" alt="logo" src={logo}/></Link>
<ul className="menu_list">
<li><Link to="/">Home</Link></li>
<li><Link to="/search">검색</Link></li>
<li><Link to="/postwrite">포스트 작성</Link></li>
<li><Link to="/login">로그인</Link></li> {/*로그인 여부 로직 구현 필요*/}
{/* { Object.keys(user).length != 0 ?
<li><Link to={`/profile/${getUserId()}`}>profile</Link>/<span onClick={logout}>logout</span></li> :
<li><Link to="/login">login</Link></li>
} */}
</ul>
<ButtonLink link='/' onclick={dont_CheckSession_And_Navigate}>
<img className="logo_image" alt="logo" src={logo}/>
</ButtonLink>
<ul>
<Welcome name={user}></Welcome>
<ul className="menu_list">
<li><ButtonLink link='/' onclick={dont_CheckSession_And_Navigate}>Home</ButtonLink></li>
<li><ButtonLink link='/search' onclick={checkSession_And_Navigate}>검색</ButtonLink></li>
<li><ButtonLink link='/postwrite' onclick={checkSession_And_Navigate}>포스트 작성</ButtonLink></li>
<li><ButtonLink link='/login' onclick={user?logOut:dont_CheckSession_And_Navigate}>{user?'로그아웃':'로그인'}</ButtonLink></li> {/*로그인 여부 로직 구현 필요*/}
{/* { Object.keys(user).length != 0 ?
<li><Link to={`/profile/${getUserId()}`}>profile</Link>/<span onClick={logout}>logout</span></li> :
<li><Link to="/login">login</Link></li>
} */}
</ul>
</ul>
</div>
);
}
async function requestLogout() {
const response = await axios({
url: 'http://localhost:8080/logout', // 통신할 웹문서
method: 'get', // 통신할 방식
});
if (response.status === 200) {
return response.data;
}
else {
return null;
}
}
export default Header;
import { useNavigate } from 'react-router-dom';
import GoogleLoginButton from "./GoogleLoginButton.js";
function Button({history, children}){
const navigate = useNavigate();
return(
<button onClick={() => {navigate('/search');}}>
{children}
</button>
);
}
function Login() {
return(
<div className="App">
<h1>로그인 '해줘'</h1>
<GoogleLoginButton></GoogleLoginButton>
</div>)
;
}
export default Login;
\ No newline at end of file
import { useNavigate } from 'react-router-dom';
import cookie from 'react-cookies';
import React, { useState, useEffect, useContext } from 'react';
function Button({history, children}){
const navigate = useNavigate();
......@@ -10,6 +12,10 @@ function Button({history, children}){
}
function Main() {
useEffect(() => {
// 컴포넌트가 불러와지면 실행이 되는군.
});
return(
<div className="App">
<h1>메인 페이지 입니다.</h1>
......
import { createContext } from 'react';
export const UserContext = createContext(null);
\ No newline at end of file
......@@ -8,7 +8,7 @@ const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<BrowserRouter>
<App/>
<App/>
</BrowserRouter>
</React.StrictMode>
);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment