From 7f0cec1dfa5955442589b775dcb522f3c46673e2 Mon Sep 17 00:00:00 2001 From: jihwooon Date: Thu, 14 Mar 2024 16:07:07 +0900 Subject: [PATCH 1/4] =?UTF-8?q?JWT=20=EC=9D=B8=EC=A6=9D=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EB=8F=84=EC=9E=85=20=EB=B0=8F=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - JWT 인증 기능을 도입했습니다. - 토큰 생성 및 검증을 위한 모듈 users/jwt/jwt.provider.ts 파일을 생성했습니다. - signin.service에서 JWT 모듈을 사용하도록 수정했습니다. - user.repository에서 사용자 정보 조회 시 ID를 포함하도록 수정했습니다. - signin.service에서 토큰 생성 시 사용자 ID를 포함하도록 수정했습니다. --- server/src/users/domain/user.repository.ts | 3 +- server/src/users/jwt/jwt.provider.ts | 31 +++++++++++++++++++ .../signin/application/signin.service.ts | 15 ++------- 3 files changed, 35 insertions(+), 14 deletions(-) create mode 100644 server/src/users/jwt/jwt.provider.ts diff --git a/server/src/users/domain/user.repository.ts b/server/src/users/domain/user.repository.ts index 49ce051..c57b406 100644 --- a/server/src/users/domain/user.repository.ts +++ b/server/src/users/domain/user.repository.ts @@ -42,7 +42,7 @@ export const save = async ({ export const findByEmail = async (email: string): Promise => { const [rows] = await doQuery((connection) => connection.execute( - `SELECT email,password,salt + `SELECT id, email,password,salt FROM users WHERE email = ?`, [email], ), @@ -55,6 +55,7 @@ export const findByEmail = async (email: string): Promise => { } return new User({ + id: row.id, email: row.email, password: row.password, salt: row.salt, diff --git a/server/src/users/jwt/jwt.provider.ts b/server/src/users/jwt/jwt.provider.ts new file mode 100644 index 0000000..8c053a7 --- /dev/null +++ b/server/src/users/jwt/jwt.provider.ts @@ -0,0 +1,31 @@ +import dotenv from 'dotenv'; +import { StatusCodes } from 'http-status-codes'; +import jwt, { type Secret } from 'jsonwebtoken'; +import HttpException from 'src/utils/httpException'; + +dotenv.config(); + +export const generateToken = (loginUser: { userId: number; email: string; password: string }) => { + const payload = { + userId: loginUser.userId, + email: loginUser.email, + }; + + return jwt.sign(payload, process.env.JWT_SECRET as Secret, { + expiresIn: '5m', + }); +}; + +export const validateToken = (token: string) => { + if (token == null || token === undefined || token === '') { + throw new HttpException(`token는 ${token}이 될 수 없습니다`, StatusCodes.BAD_REQUEST); + } + + try { + const decodedJwt = jwt.verify(token, process.env.JWT_SECRET as Secret) as jwt.JwtPayload; + + return decodedJwt; + } catch (e) { + throw new HttpException('인증 할 수 없는 token 입니다', StatusCodes.UNAUTHORIZED); + } +}; diff --git a/server/src/users/signin/application/signin.service.ts b/server/src/users/signin/application/signin.service.ts index 842748d..0fe5374 100644 --- a/server/src/users/signin/application/signin.service.ts +++ b/server/src/users/signin/application/signin.service.ts @@ -1,21 +1,9 @@ -import jwt from 'jsonwebtoken'; - import { StatusCodes } from 'http-status-codes'; import { isMatchPassword } from 'src/users/domain/password.provider'; import { findByEmail } from 'src/users/domain/user.repository'; +import { generateToken } from 'src/users/jwt/jwt.provider'; import HttpException from 'src/utils/httpException'; -const generateToken = (loginUser: { email: string; password: string }) => - jwt.sign( - { - email: loginUser.email, - }, - '1235467898910', - { - expiresIn: '5m', - }, - ); - const signinService = async (email: string, password: string): Promise<{ accessToken: string }> => { const loginUser = await findByEmail(email); if (!loginUser) { @@ -29,6 +17,7 @@ const signinService = async (email: string, password: string): Promise<{ accessT } const token = generateToken({ + userId: userData.id, email: userData.email, password: userData.password, }); From 0f6e1cd1c1e4719267a74bf9976e3b69060d2f5b Mon Sep 17 00:00:00 2001 From: jihwooon Date: Thu, 14 Mar 2024 16:36:09 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=EC=A2=8B=EC=95=84=EC=9A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20-=20JWT=20=EC=9D=B8=EC=A6=9D=20=EB=B0=8F=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 좋아요 기능을 사용자 인증과 연동했습니다. - addLike 서비스에서 사용자 ID 를 받는 방식을 엑세스 토큰으로 변경했습니다. - users/jwt/jwt.provider.ts 의 validateToken 함수를 사용하여 엑세스 토큰을 검증하고 사용자 ID 를 추출합니다. - add-like.controller.ts 에서 요청 헤더에서 엑세스 토큰을 가져오도록 수정했습니다. --- server/src/likes/application/add-like.service.ts | 5 ++++- server/src/likes/web/add-like.controller.ts | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/server/src/likes/application/add-like.service.ts b/server/src/likes/application/add-like.service.ts index cde9b1c..730f7fb 100644 --- a/server/src/likes/application/add-like.service.ts +++ b/server/src/likes/application/add-like.service.ts @@ -1,10 +1,13 @@ import { StatusCodes } from 'http-status-codes'; import HttpException from 'src/utils/httpException'; +import { validateToken } from 'src/users/jwt/jwt.provider'; + import Like from '../domain/like'; import { save } from '../domain/likes.repository'; -export const addLike = async (userId: number, likedBookId: number): Promise => { +export const addLike = async (accessToken: any, likedBookId: number): Promise => { + const { userId } = validateToken(accessToken); const like = new Like({ userId, likedBookId }); const likeData = like.getDataOfLike(); diff --git a/server/src/likes/web/add-like.controller.ts b/server/src/likes/web/add-like.controller.ts index 2d99870..311a61b 100644 --- a/server/src/likes/web/add-like.controller.ts +++ b/server/src/likes/web/add-like.controller.ts @@ -5,8 +5,9 @@ import { StatusCodes } from 'http-status-codes'; import { addLike } from '../application/add-like.service'; -const addLikeHandler = ({ params: { id }, body: { userId } }: Request, res: Response) => { - ResponseHandler(() => addLike(userId, Number(id)), StatusCodes.CREATED, res); +const addLikeHandler = ({ params: { id }, headers }: Request, res: Response) => { + const accessToken = headers.authorization?.split('Bearer ')[1]; + ResponseHandler(() => addLike(accessToken, Number(id)), StatusCodes.CREATED, res); }; export default addLikeHandler; From 125ca4b43b49ff110883d938b26b4f39c58635c8 Mon Sep 17 00:00:00 2001 From: jihwooon Date: Thu, 14 Mar 2024 17:37:00 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20API=20?= =?UTF-8?q?=EB=B0=8F=20=EC=83=81=ED=83=9C=20=EA=B4=80=EB=A6=AC=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20-=20JWT=20=ED=86=A0=ED=81=B0=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로그인 API 응답 형식을 변경하여 JWT 토큰을 반환하도록 수정했습니다. - 이전에는 단순 문자열 데이터를 반환했으나 이번 변경을 통해 JWT 토큰을 반환합니다. 클라이언트 코드에서 반환받은 JWT 토큰을 저장하고 관리합니다. - client/src/api/auth.api.ts 에서 응답 데이터에서 token 필드를 추출합니다. - client/src/pages/Signin.tsx 에서 로그인 성공 시 받은 토큰을 storeLogin 함수를 통해 저장합니다. - 서버 코드에서도 로그인 성공 시 생성된 JWT 토큰을 응답 객체에 포함하도록 변경했습니다. - server/src/users/signin/web/signin.controller.ts 에서 반환하는 객체에 token 필드를 추가합니 --- client/src/api/auth.api.ts | 2 +- client/src/pages/Signin.tsx | 2 +- server/src/users/signin/web/signin.controller.ts | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/src/api/auth.api.ts b/client/src/api/auth.api.ts index ef12635..f839e3d 100644 --- a/client/src/api/auth.api.ts +++ b/client/src/api/auth.api.ts @@ -21,7 +21,7 @@ export const resetPassword = async (data: SignupProps) => { } interface SiginResponse { - data: string; + token: string; } export const signin = async (data: SigninProps) => { diff --git a/client/src/pages/Signin.tsx b/client/src/pages/Signin.tsx index dde0bb6..dbd8dc4 100644 --- a/client/src/pages/Signin.tsx +++ b/client/src/pages/Signin.tsx @@ -22,7 +22,7 @@ const Signin = () => { const onSubmit = (data: SigninProps) => { signin(data).then((res) => { - storeLogin(res.data) + storeLogin(res.token) showAlert('로그인 완료되었습니다.') navigate('/') }, (error) => { diff --git a/server/src/users/signin/web/signin.controller.ts b/server/src/users/signin/web/signin.controller.ts index 5b3251b..bd18b25 100644 --- a/server/src/users/signin/web/signin.controller.ts +++ b/server/src/users/signin/web/signin.controller.ts @@ -14,7 +14,9 @@ const signinController = async (req: Request, res: Response) => { httpOnly: true, }); - return accessToken; + return { + token: accessToken, + }; }; ResponseHandler(signInFunction, StatusCodes.OK, res); From 88a594fd33898cd471a37d32aa192dfc6a92e845 Mon Sep 17 00:00:00 2001 From: jihwooon Date: Thu, 14 Mar 2024 22:09:27 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=EC=A2=8B=EC=95=84=EC=9A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20-=20=EC=9D=B8=EC=A6=9D=20=ED=97=A4=EB=8D=94=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 좋아요 기능 호출 시 인증 헤더 검증 방식을 보완했습니다. - 이전에는 Bearer 스키마를 포함한 전체 인증 헤더 문자열을 사용하였습니다. - 이번 변경을 통해 Bearer 스키마 뒤의 공백을 제거하고 실제 토큰 값만 추출하도록 수정했습니다. --- server/src/likes/web/add-like.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/likes/web/add-like.controller.ts b/server/src/likes/web/add-like.controller.ts index 311a61b..b8b365a 100644 --- a/server/src/likes/web/add-like.controller.ts +++ b/server/src/likes/web/add-like.controller.ts @@ -6,7 +6,7 @@ import { StatusCodes } from 'http-status-codes'; import { addLike } from '../application/add-like.service'; const addLikeHandler = ({ params: { id }, headers }: Request, res: Response) => { - const accessToken = headers.authorization?.split('Bearer ')[1]; + const accessToken = headers.authorization; ResponseHandler(() => addLike(accessToken, Number(id)), StatusCodes.CREATED, res); };