Jieunny의 블로그
S3) Unit 7. [실습] Token & Cookie 사용해서 로그인 기능 구현하기 본문
📣 Cookie와 Token을 사용해서 로그인 기능 구현하기
- Client 부분은 Cookie 게시물과 같으므로 Server 부분만 설명.
𝟭. 서버 파일 구조
controllers
ㄴ helper
ㄴ tokenFunctions.js : 토큰 생성, 토큰 검증 함수 구현
ㄴ users
ㄴ login.js : server의 login controller 구현
1. request로 받은 id, password와 일치하는 정보가 DB에 있는지 확인
2. 없으면 요청을 거절, 있으면 필요한 데이터를 담은 두 종류의 토큰 생성(access, refresh)
3. request로 받은 checkedLoginKeep 여부 확인
4. true인 경우 accessToken, refreshToken 둘 다 쿠키로 설정하고, false인 경우 accessToken만 쿠키로 설정
5. 클라이언트에 바로 응답을 보내지 않고 /userInfo로 리다이렉트
ㄴ userInfo.js : server의 userinfo controller 구현
1. 쿠키에 accessToken이 있으면 검증, 검증 되면 토큰에 담긴 정보를 이용해 DB에서 정보 조회 후 응답으로 전달
2. accessToken이 검증되지 않으면 refreshToken 검증, 검증 되면 토큰에 담긴 정보를 이용해 DB에서 정보 조회 후 응답으로
전달, refreshToken을 이용해 accessToken 재발급 해서 쿠키에 담고 정보와 함께 응답으로 전달
3. refreshToken도 검증되지 않았다면 요청 거절
4. accessToken, refreshToken이 모두 존재하지 않는다면 요청 거절
ㄴ logout.js : server의 userinfo controller 구현
1. 클라이언트에 저장된 쿠키의 값을 초기화
ㄴ .env : 환경 변수 설정
𝟮. 코드 구현
📌 login.js
const { USER_DATA } = require('../../db/data');
// JWT는 generateToken으로 생성할 수 있습니다. 먼저 tokenFunctions에 작성된 여러 메서드들의 역할을 파악하세요.
const { generateToken } = require('../helper/tokenFunctions');
module.exports = async (req, res) => {
const { userId, password } = req.body.loginInfo;
// 입력한 id, password 받아오기
const { checkedKeepLogin } = req.body;
// 로그인 유지에 체크했는지 여부 받아오기
// checkedKeepLogin이 false라면 Access Token만 보내야합니다.
// checkedKeepLogin이 true라면 Access Token과 Refresh Token을 함께 보내야합니다.
const userInfo = {
...USER_DATA.filter((user) => user.userId === userId && user.password === password)[0],
// 입력한 로그인 정보와 같은 정보를 가진 유저 정보만 가져오기
};
const cookiesOption = {
// 쿠키와 함께 전달해줄 쿠키 옵션 선언
domain: 'localhost',
path: '/',
httpOnly: true,
sameSite: 'none',
secure: true,
}
if(userInfo.id === undefined){
// 유저 정보가 없다면 -> 로그인 실패했을 때
res.status(401).send('Not Authorized');
} else {
// 로그인 성공했을 때
const accessToken = generateToken(userInfo, checkedKeepLogin).accessToken;
const refreshToken = generateToken(userInfo, checkedKeepLogin).refreshToken;
// 토큰 두 종류 발급받기
if(checkedKeepLogin === true) {
// 로그인 유지해야 하는 경우 쿠키 옵션에 영속성 주기
cookiesOption.maxAge = 1000 * 60 * 30
cookiesOption.expires = new Date(Date.now() + (1000 * 60 * 30) )
res.cookie('access_jwt', accessToken, cookiesOption);
res.cookie('refresh_jwt', refreshToken, cookiesOption);
// 로그인 유지해야 하므로 두 종류 쿠키 모두 응답으로 전달
} else {
// 로그인 유지 안하므로 accessToken이 담긴 쿠키만 전달
res.cookie('access_jwt', accessToken, cookiesOption);
}
}
res.redirect("/userinfo");
// userInfo로 리다이렉션(요청 다시 보내기)
};
📌 userInfo.js
const { USER_DATA } = require('../../db/data');
// JWT는 verifyToken으로 검증할 수 있습니다. 먼저 tokenFunctions에 작성된 여러 메서드들의 역할을 파악하세요.
const { verifyToken, generateToken } = require('../helper/tokenFunctions');
module.exports = async (req, res) => {
const accessToken = req.cookies.access_jwt;
const refreshToken = req.cookies.refresh_jwt;
// 생성한 쿠키(토큰이 담김) 받아오기
if(accessToken){
// 액세스 토큰 있으면
if(verifyToken('access', accessToken)===null){
// 액세스 토큰 검증 -> 검증 안됨.
if(verifyToken('refresh', refreshToken)===null){
// 리프레시 토큰 검증 -> 검증 안됨.
res.status(401).send('Not Authorized');
// 로그인 실패!
}
else {
//리프레시 토큰 검증 됨.
const cookiesOption = {
// 액세스토큰 재발급 시 필요한 쿠키 옵션 선언
domain: 'localhost',
path: '/',
httpOnly: true,
sameSite: 'none',
secure: true,
}
const decoded = verifyToken('refresh', refreshToken);
// 리프레시 토큰 검증으로 복호화하기 -> 필요한 정보 가져오기
const findUser = {
...USER_DATA.filter((user) => user.id === decoded.id)[0],
// DB에서 일치하는 정보를 가진 요소만 받아오기
};
let payload = {
// 액세스 토큰 발급시 필요한 정보 선언(아까 뽑아온 유저 정보 이용)
id: findUser.id,
userId: findUser.userId,
password: findUser.password,
email: findUser.email,
name: findUser.name,
position: findUser.position,
location: findUser.location,
bio: findUser.bio,
}
let checkedKeepLogin = req.checkedKeepLogin;
const accessToken = generateToken(payload, checkedKeepLogin).accessToken;
// 액세스 토큰 재발급
res.cookie('access_jwt', accessToken, cookiesOption);
// 재발급한 토큰 쿠키로 보내주기
delete findUser.password;
// 응답으로 유저 정보 보내기 전에 민감한 정보인 password 삭제
res.send(findUser);
}
} else {
// 액세스 토큰 검증 됨.
const decoded = verifyToken('access', accessToken);
// 액세스 토큰 검증으로 복호화 하기
const findUser = {
...USER_DATA.filter((user) => user.id === decoded.id)[0],
};
delete findUser.password;
res.send(findUser);
}
}
else{
// 액세스 토큰 없음.
if(!refreshToken) res.status(401).send('Not Authorized');
// 리프레시 토큰도 없음 -> 로그인 실패!
}
};
📌 logout.js
module.exports = (req, res) => {
const cookiesOption = {
// 삭제할 쿠키 옵션 선언
domain: 'localhost',
path: '/',
httpOnly: true,
sameSite: 'none',
secure: true,
}
const refreshToken = req.cookies.refreshToken;
// 리프레시 토큰이 있으면 얘도 같이 삭제해주어야 하므로 받아오기
res.status(205).clearCookie('access_jwt', cookiesOption).send("logout");
// 액세스 토큰이 담긴 쿠키 삭제
if(refreshToken){
// 리프레시 토큰이 있다면
res.status(205).clearCookie('refresh_jwt', cookiesOption).send("logout");
// 리프레시 토큰이 담긴 쿠키도 삭제
}
};
𝟯. 시연화면
'CodeStates > Training' 카테고리의 다른 글
S3) Unit 8. [실습] Coz'Mini Hackaton (Todo-List) (2) | 2023.03.10 |
---|---|
S3) Unit 7. [실습] OAuth(깃허브) 사용해서 로그인 기능 구현하기 (0) | 2023.03.09 |
S3) Unit 7. [실습] Cookie로 로그인 기능 구현하기 (0) | 2023.03.07 |
S3) Unit 4. [실습] CMarket Redux (0) | 2023.02.27 |
S3) Unit 4. [실습] CMarket Hooks (2) | 2023.02.24 |