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 @@ ...@@ -14,6 +14,8 @@
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"express": "^4.18.2", "express": "^4.18.2",
"express-session": "^1.17.3",
"memorystore": "^1.6.7",
"moment": "^2.29.4", "moment": "^2.29.4",
"mongoose": "^8.0.1", "mongoose": "^8.0.1",
"nodemon": "^3.0.1" "nodemon": "^3.0.1"
...@@ -504,6 +506,32 @@ ...@@ -504,6 +506,32 @@
"node": ">= 0.10.0" "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": { "node_modules/fill-range": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
...@@ -803,6 +831,53 @@ ...@@ -803,6 +831,53 @@
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" "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": { "node_modules/merge-descriptors": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
...@@ -1087,6 +1162,14 @@ ...@@ -1087,6 +1162,14 @@
"node": ">= 0.8" "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": { "node_modules/parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
...@@ -1128,6 +1211,11 @@ ...@@ -1128,6 +1211,11 @@
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" "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": { "node_modules/pstree.remy": {
"version": "1.1.8", "version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
...@@ -1155,6 +1243,14 @@ ...@@ -1155,6 +1243,14 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/range-parser": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
...@@ -1396,6 +1492,17 @@ ...@@ -1396,6 +1492,17 @@
"node": ">= 0.6" "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": { "node_modules/undefsafe": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"express": "^4.18.2", "express": "^4.18.2",
"express-session": "^1.17.3",
"memorystore": "^1.6.7",
"moment": "^2.29.4", "moment": "^2.29.4",
"mongoose": "^8.0.1", "mongoose": "^8.0.1",
"nodemon": "^3.0.1" "nodemon": "^3.0.1"
......
...@@ -4,7 +4,7 @@ import path from 'path' ...@@ -4,7 +4,7 @@ import path from 'path'
import process from 'process' import process from 'process'
import moment from 'moment'; import moment from 'moment';
import cookieParser from 'cookie-parser'; import cookieParser from 'cookie-parser';
import session from 'express-session';
import db from './db.js' import db from './db.js'
const app = express(); const app = express();
...@@ -14,6 +14,18 @@ db.connectDB(); ...@@ -14,6 +14,18 @@ db.connectDB();
app.use(express.static(path.join(process.cwd(), '../frontend/build'))); 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(cookieParser());
app.use(express.json()); app.use(express.json());
app.use(express.urlencoded({ extended: false })); app.use(express.urlencoded({ extended: false }));
...@@ -37,19 +49,63 @@ function encode_utf8(s) { ...@@ -37,19 +49,63 @@ function encode_utf8(s) {
return unescape(encodeURIComponent(s)); return unescape(encodeURIComponent(s));
} }
app.post('/login', (req, res) => { app.post('/login', async (req, res) => {
/* /*
TODO: 토큰의 무결성 체크 TODO: 토큰의 무결성 체크
토큰이 이상이 없다면, 로그인/회원가입 로직을 수행 후 jwt 쿠키를 보낸다. 토큰이 이상이 없다면, 로그인/회원가입 로직을 수행 후 jwt 쿠키를 보낸다.
*/ */
const expires = moment().add('2','m').toDate() const expires = moment().add('1','m').toDate()
res.cookie('jwt', JSON.stringify(req.body), {expires});
// 정보가 없다면 회원 가입 (강제?)
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); 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,19 +2,27 @@ import "./App.css"; ...@@ -2,19 +2,27 @@ import "./App.css";
import {Routes, Route } from 'react-router-dom'; import {Routes, Route } from 'react-router-dom';
import Header from "./Header.js"; import Header from "./Header.js";
import Main from "./Main.js"; import Main from "./Main.js";
import Login from "./Login.js";
import Search from "./Search.js"; import Search from "./Search.js";
import GoogleLoginButton from "./GoogleLoginButton.js"; import GoogleLoginButton from "./GoogleLoginButton.js";
import React, { useEffect, useState, useContext} from 'react';
import { UserContext } from './Usercontext.js';
function App() { function App() {
const [ myName, setMyName] = useState(null);
return( return(
<div className="App"> <div className="App">
<Header/> <UserContext.Provider value={setMyName}>
<Header user={myName}></Header>
<Routes> <Routes>
<Route path="/" element={<Main/>}></Route> <Route path="/" element={<Main/>}></Route>
<Route path="/login" element={<Login/>}></Route>
<Route path="/search" element={<Search/>}></Route> <Route path="/search" element={<Search/>}></Route>
{/* <Route path="/postwrite" element={<PostWrite/>}></Route> */} {/* <Route path="/postwrite" element={<PostWrite/>}></Route> */}
</Routes> </Routes>
<GoogleLoginButton/> </UserContext.Provider>
{/* <Footer/> */} {/* <Footer/> */}
</div> </div>
); );
......
...@@ -2,6 +2,8 @@ import { GoogleLogin } from "@react-oauth/google"; ...@@ -2,6 +2,8 @@ import { GoogleLogin } from "@react-oauth/google";
import { GoogleOAuthProvider } from "@react-oauth/google"; import { GoogleOAuthProvider } from "@react-oauth/google";
import { useNavigate, Navigate } from "react-router-dom"; import { useNavigate, Navigate } from "react-router-dom";
import { useCookies } from 'react-cookie' import { useCookies } from 'react-cookie'
import React, { useEffect, useState, useContext} from 'react';
import { UserContext } from './Usercontext.js';
// 안써도 자동으로 한국 시간을 불러온다. 명확하게 하기 위해 import // 안써도 자동으로 한국 시간을 불러온다. 명확하게 하기 위해 import
import moment from 'moment'; import moment from 'moment';
...@@ -11,37 +13,24 @@ import base64 from 'base-64'; ...@@ -11,37 +13,24 @@ import base64 from 'base-64';
import axios from 'axios'; import axios from 'axios';
axios.defaults.withCredentials = true; axios.defaults.withCredentials = true;
/*
*/
const GoogleLoginButton = () => {
const setUserName = useContext(UserContext);
const GoogleLoginButton = () => {
const [cookies, setCookie, removeCookie] = useCookies(); const [cookies, setCookie, removeCookie] = useCookies();
const clientId = '716858812522-rb0pfisq317unkh4so5hvbu16p19kqp8.apps.googleusercontent.com' const clientId = '716858812522-rb0pfisq317unkh4so5hvbu16p19kqp8.apps.googleusercontent.com'
const navigate = useNavigate(); const navigate = useNavigate();
const goMain = () => { const goMain = () => {
navigate("/main"); navigate("/");
} }
return ( return (
<> <>
<GoogleOAuthProvider clientId={clientId}> <GoogleOAuthProvider clientId={clientId}>
<GoogleLogin <GoogleLogin
onSuccess={(res) => { 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('.') let datas = res.credential.split('.')
const obj = JSON.parse(b64DecodeUnicode(datas[1])); const obj = JSON.parse(b64DecodeUnicode(datas[1]));
...@@ -49,11 +38,13 @@ const GoogleLoginButton = () => { ...@@ -49,11 +38,13 @@ const GoogleLoginButton = () => {
// 토큰을 보내 로그인 로직 처리 // 토큰을 보내 로그인 로직 처리
// 로그인이 정상적으로 되었다면 쿠키를 등록 // 로그인이 정상적으로 되었다면 쿠키를 등록
let response = requestLogin(obj); let response = requestLogin(obj).then(
console.log(cookies.jwt) (val) => {
if (response) { setUserName(val)
goMain(); goMain();
} }
);
}} }}
onFailure={(err) => { onFailure={(err) => {
console.log("Login Failed"); console.log("Login Failed");
...@@ -79,7 +70,6 @@ async function requestLogin(payloadObj) { ...@@ -79,7 +70,6 @@ async function requestLogin(payloadObj) {
method: 'post', // 통신할 방식 method: 'post', // 통신할 방식
data: payloadObj data: payloadObj
}); });
console.log(response)
if (response.status === 200) { if (response.status === 200) {
return response.data; return response.data;
} }
......
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import logo from './logo.png'; import logo from './logo.png';
import './Header.css'; 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 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('/');
}
}
function Header(){
return( return(
<div className="header"> <div className="header">
<Link to='/'><img className="logo_image" alt="logo" src={logo}/></Link> <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"> <ul className="menu_list">
<li><Link to="/">Home</Link></li> <li><ButtonLink link='/' onclick={dont_CheckSession_And_Navigate}>Home</ButtonLink></li>
<li><Link to="/search">검색</Link></li> <li><ButtonLink link='/search' onclick={checkSession_And_Navigate}>검색</ButtonLink></li>
<li><Link to="/postwrite">포스트 작성</Link></li> <li><ButtonLink link='/postwrite' onclick={checkSession_And_Navigate}>포스트 작성</ButtonLink></li>
<li><Link to="/login">로그인</Link></li> {/*로그인 여부 로직 구현 필요*/} <li><ButtonLink link='/login' onclick={user?logOut:dont_CheckSession_And_Navigate}>{user?'로그아웃':'로그인'}</ButtonLink></li> {/*로그인 여부 로직 구현 필요*/}
{/* { Object.keys(user).length != 0 ? {/* { Object.keys(user).length != 0 ?
<li><Link to={`/profile/${getUserId()}`}>profile</Link>/<span onClick={logout}>logout</span></li> : <li><Link to={`/profile/${getUserId()}`}>profile</Link>/<span onClick={logout}>logout</span></li> :
<li><Link to="/login">login</Link></li> <li><Link to="/login">login</Link></li>
} */} } */}
</ul> </ul>
</ul>
</div> </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; 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 { useNavigate } from 'react-router-dom';
import cookie from 'react-cookies';
import React, { useState, useEffect, useContext } from 'react';
function Button({history, children}){ function Button({history, children}){
const navigate = useNavigate(); const navigate = useNavigate();
...@@ -10,6 +12,10 @@ function Button({history, children}){ ...@@ -10,6 +12,10 @@ function Button({history, children}){
} }
function Main() { function Main() {
useEffect(() => {
// 컴포넌트가 불러와지면 실행이 되는군.
});
return( return(
<div className="App"> <div className="App">
<h1>메인 페이지 입니다.</h1> <h1>메인 페이지 입니다.</h1>
......
import { createContext } from 'react';
export const UserContext = createContext(null);
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment