출처 https://brunch.co.kr/@jinyoungchoi95/1
이메일형식을 이렇게 지정해놨다면
ddd@naver.com
유효성 검사를 통과했다 != 로그인에 성공했다
유효성 검사를 통과했다 => 이 값을 서버에 전달할 수 있겠다
react -> express -> mysql
값이 있을때 : 응답(200)
값이 없을때 : 응답은 서버 만드는사람 마음대로(404나 오류 메세지)
로그인후 로그인한 사람의 정보가 있어야하는데...
어떻게?
과거: express에 session이라는 저장공간을 만들어 저장해놨음
세션 id를 react 한테 줌
게시글을 볼때 지금 로그인한 사람의 세션 id 까지 express에 전달.문제점: 사용자가 많아졌을땐 컴퓨터(서버) 한대로는 처리가 안됨..5대로 돌려야되는데 그럼 저장된 정보를 불러오는데 문제가 생김(서버간 세션 공유가 안되기 때문에)
그럼 데이터베이스는 하나니까 로그인한 사람의 정보를 DB에 저장을 해놓자! 한 가지 문제점?
데이터베이스도 여러개를 만들 수가 있다(scale out)
그럼 로그인한 정보를 react가 가지고있음 되는거 아냐?
로그인 성공했다는 의미로 증명서를 발급해줌(토큰)
자 여기 너가 가지고 있어하고 react에게 줌
react가 express에게 넘겨주면서
토큰 검사(유효하면 mysql가서 게시글 가져옴)를 함
express는 mysql에서 해당 사용자 있는지 확인
사용자가 있다면 jwt 토큰 발급하여 react로 전달
react jwt토큰 저장해놓고
앞으로 로그인 한 사람만 접근할 수 있는 페이지가 있다면
jwt토큰을 함께 담아서 요청
express에서는 jwt토큰을 받아서, 토큰이 유효한지 검사하여
해당 토큰이 유효하면 응답하고,
유효하지 않으면 오류 발생(권한이 없습니다 로그인을 다시 해주세요!)
잘 생각해보면 JS 객체에다 저장하는게 가장 편리함
객체를 암호화해서 토큰을 만들 수 있음
점을 기반으로 HEADER(어떤 방식으로 암호화를 했는지 ALGORITHM & TOKEN TYPE 여러가지 정보들을 넣는 공간).PAYROAD(실질적으로 넣고싶은 DATA).VERIFY SIGNATURE(얘가 위조가 됐는지 안됐는지)
원래 비밀번호 암호화한것은 복호화가 불가능(단방향 암호화)
그런데 JWT는 복호화가 가능하다(절대 비밀번호 넣지 않는다)
누가 로그인했는지 알려면 주요 KEY값으로 EMAIL을 넣는다
npm install jsonwebtoken
const jwt = require("jsonwebtoken"); // 옛날방식의 import
1.Sign synchronous 동기적으로(차례대로)
sign()
2.Sign asynchronously 비동기적으로
1번 사용
jwt.sign( {DATA}, '어떤 KEY를 가지고?(노출 XXX)', {OPTION-어떤 알고리즘으로?지속시간은?} )
let myToken = jwt.sign( {email:'abc@naver.com'}, 'tokenpw', {expiresIn:'1h'} )
"iat" : issued at
"exp" : expired
문제는 회원가입시 비밀번호가 DB에 bcrypt를 통해 암호화되서 들어감
리액트 mysql
ab@ab.com ab@ab.com
123456 2b$10TcZOlfVlXOWfz.7XIuBW5u8wUKM0VS/C5UdZ082VITw.PiGlCpAfa
ab@ab.com
123456 -> 암호화해서 회원가입할때 암호화된 애랑 비교하면 되지 않을까? bcrypt 는 보안이 철저해서 똑같이 암호화가 되지 않는다.
ab@ab.com이메일 가진 녀석을 찾아서
암호화된 비밀번호 가져오기
bcrypt(일반 비밀번호, 암호화 된 비밀번호)
비밀번호를 맞게쓰면 토큰이 나온다
이 토큰을 변수에 저장하면 큰일나는 이유?
LoginPage 함수 안에 저장해봤자 다른 페이지로 이동하면 사라져버리기 때문에...
그래서 브라우저에 저장공간이 있다(local storage, Session storage, Cookies)
어떤걸 사용하든 본인의 취향 차이(보안쪽으로 가게되면 세세한 차이가 있다)
state변수는 만들어져있는 컴포넌트 안에서밖에 사용을 못한다
token은 a컴포넌트, b컴포넌트, c컴포넌트에서 모두 사용을 해야하는데..?
전역 스테이트변수는 스테이트변수는 스테이트변수인데
모든 컴포넌트에서 접근할 수 있는 스테이트변수를 의미한다
redux, context api, ....
createContext를 활용하여
ex)
const ABC = createContext();
감싸준다(provider로 감싼 자식들에서는 모두 사용가능)
ex)
<ABC.Provider>
이 안에 그려지는 자식 컴포넌트에서는 모두 값 접근가능
</ABC.Provider>
provider의 value에다 넘겨준다
ex)
<ABC.Provider value={ {a:값, b:값, c:값} }>
이 안에 그려지는 자식 컴포넌트에서는 모두
{a:값, b:값, c:값} 값 접근가능
</ABC.Provider>
궁금한게...
꼭 value라는 이름을 써야할까?
무조건 value = {} 에 들어있는 값만 사용가능
ex)
const value = useContext(ABC);
구조분해할당도 사용가능
const {b, c} = useContext(ABC);
전문이 너무 길어 추가된 부분만 명시 해놓았습니다.
import { createContext, useState } from 'react';
const router = createBrowserRouter([
.
.
.
// 사용자에 대한 토큰 만들기
export const UserContext = createContext();
const App = ()=>{
// 전역에서 사용할 변수
const [ accessToken, setAccessToken ] = useState(null);
return (
<UserContext.Provider value={ { accessToken, setAccessToken } }>
<RouterProvider router={router}/>
</UserContext.Provider>
);
}
import { useContext, useState } from "react";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import { UserContext } from "../../App";
const LoginPage = ()=>{
const navigate = useNavigate();
// App.js에 있는 accessToken 변수와 setAccessToken함수 사용하기
const {setAccessToken} = useContext(UserContext);
.
.
.
const onLoginSubmit = async (e)=>{
if(check){
// 로그인한 회원 조회
try{
let res = await axios.post('/api/login', {email, password});
alert(res.data.accessToken); // 비밀번호를 맞게쓰면 토큰이 나온다
// 이 토큰을 변수에 저장하면 큰일나는 이유?
// LoginPage 함수 안에 저장해봤자 다른 페이지로 이동하면 사라져버리기 때문에
// 그래서 브라우저에 저장공간이 있다(local storage, Session storage, Cookies)
localStorage.setItem('accessToken', res.data.accessToken);
setAccessToken(res.data.accessToken);
navigate('/', {replace:false}); // 뒤로가기 못하게
}catch(err){
console.log(err);
if(err.response.status === 404){
alert('아이디 또는 비밀번호를 확인해주세요');
}
JWT_SECRET=board23
점점 어려워 지는구나~~~헤롱@_@