From eac669202adc673db111a579cf3f0aabff07368f Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Wed, 25 Dec 2024 01:59:09 +0900 Subject: [PATCH 01/13] =?UTF-8?q?[FEAT]=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..c9355301 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# ๐ŸŒ€ Spring MVC (์ธ์ฆ) + +## ๋กœ๊ทธ์ธ +#### ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ + + ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ +#### ๋กœ๊ทธ์ธ ์š”์ฒญ + + ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ -> ๋ฉค๋ฒ„ ์กฐํšŒ + + ์กฐํšŒํ•œ ๋ฉค๋ฒ„๋กœ ํ† ํฐ ๋ฐœ๊ธ‰ + + Cookie ๋ฅผ ๋งŒ๋“ค์–ด ์‘๋‹ต +#### ์ธ์ฆ ์ •๋ณด ์กฐํšŒ + + Cookie -> ํ† ํฐ ์ •๋ณด ์ถ”์ถœ + + ๋ฉค๋ฒ„๋ฅผ ์ฐพ์•„์„œ ์‘๋‹ต + +๋กœ๊ทธ์ธ ๊ด€๋ จ API ++ GET/login : ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ํ˜ธ์ถœ ++ POST/login : ๋กœ๊ทธ์ธ ์š”์ฒญ ++ GET/login/check : ์ธ์ฆ ์ •๋ณด ์กฐํšŒ + From e1b4ce3178ea0c831ef085b1c6ae77113ff6f299 Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Thu, 26 Dec 2024 01:22:26 +0900 Subject: [PATCH 02/13] =?UTF-8?q?[FEAT]=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?(1=EB=8B=A8=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +++ src/main/java/roomescape/auth/JWTUtils.java | 36 +++++++++++++++++++ src/main/java/roomescape/auth/UserClaims.java | 6 ++++ src/main/java/roomescape/auth/UserToken.java | 6 ++++ .../java/roomescape/member/LoginRequest.java | 7 ++++ .../roomescape/member/MemberController.java | 18 ++++++++++ .../java/roomescape/member/MemberService.java | 34 +++++++++++++++--- src/main/resources/application.properties | 2 +- src/test/java/roomescape/MissionStepTest.java | 13 +++++-- 9 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 src/main/java/roomescape/auth/JWTUtils.java create mode 100644 src/main/java/roomescape/auth/UserClaims.java create mode 100644 src/main/java/roomescape/auth/UserToken.java create mode 100644 src/main/java/roomescape/member/LoginRequest.java diff --git a/build.gradle b/build.gradle index 8d52aebc..21a9445b 100644 --- a/build.gradle +++ b/build.gradle @@ -23,6 +23,11 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' implementation 'io.jsonwebtoken:jjwt-gson:0.11.2' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' diff --git a/src/main/java/roomescape/auth/JWTUtils.java b/src/main/java/roomescape/auth/JWTUtils.java new file mode 100644 index 00000000..9f710ab5 --- /dev/null +++ b/src/main/java/roomescape/auth/JWTUtils.java @@ -0,0 +1,36 @@ +package roomescape.auth; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import roomescape.member.Member; + +@Component +public class JWTUtils { + + @Value("${roomescape.auth.jwt.secret}") + private String secretKey; + + public UserToken createToken(Member member){ + String accessToken = Jwts.builder() + .setSubject(member.getId().toString()) + .claim("name", member.getName()) + .claim("role", member.getRole()) + .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) + .compact(); + + return new UserToken(accessToken); + } + + public UserClaims getClaimsFromToken(String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) + .build() + .parseClaimsJws(token) + .getBody(); + + return new UserClaims(claims.get("name", String.class)); + } +} diff --git a/src/main/java/roomescape/auth/UserClaims.java b/src/main/java/roomescape/auth/UserClaims.java new file mode 100644 index 00000000..c2070c65 --- /dev/null +++ b/src/main/java/roomescape/auth/UserClaims.java @@ -0,0 +1,6 @@ +package roomescape.auth; + +public record UserClaims( + String name +) { +} diff --git a/src/main/java/roomescape/auth/UserToken.java b/src/main/java/roomescape/auth/UserToken.java new file mode 100644 index 00000000..ce2beb5b --- /dev/null +++ b/src/main/java/roomescape/auth/UserToken.java @@ -0,0 +1,6 @@ +package roomescape.auth; + +public record UserToken( + String token +) { +} diff --git a/src/main/java/roomescape/member/LoginRequest.java b/src/main/java/roomescape/member/LoginRequest.java new file mode 100644 index 00000000..fe3676fd --- /dev/null +++ b/src/main/java/roomescape/member/LoginRequest.java @@ -0,0 +1,7 @@ +package roomescape.member; + +public record LoginRequest( + String email, + String password +) { +} diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 881ae5e0..19dee8e6 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -1,13 +1,17 @@ package roomescape.member; import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import roomescape.auth.UserClaims; +import roomescape.auth.UserToken; import java.net.URI; @@ -25,6 +29,20 @@ public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member); } + @PostMapping("/login") + public void login(@RequestBody LoginRequest loginRequest, HttpServletResponse response){ + String token = memberService.getToken(loginRequest).token(); + Cookie cookie = new Cookie("token", token); + cookie.setHttpOnly(true); + cookie.setPath("/"); + response.addCookie(cookie); + } + + @GetMapping("/login/check") + public UserClaims checkLogin(@CookieValue("token") String token){ + return memberService.checkLogin(new UserToken(token)); + } + @PostMapping("/logout") public ResponseEntity logout(HttpServletResponse response) { Cookie cookie = new Cookie("token", ""); diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index ccaa8cba..089b6784 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -1,17 +1,41 @@ package roomescape.member; +import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; +import roomescape.auth.JWTUtils; +import roomescape.auth.UserClaims; +import roomescape.auth.UserToken; @Service +@RequiredArgsConstructor public class MemberService { - private MemberDao memberDao; - - public MemberService(MemberDao memberDao) { - this.memberDao = memberDao; - } + private final MemberDao memberDao; + private final JWTUtils jwtUtils; public MemberResponse createMember(MemberRequest memberRequest) { Member member = memberDao.save(new Member(memberRequest.getName(), memberRequest.getEmail(), memberRequest.getPassword(), "USER")); return new MemberResponse(member.getId(), member.getName(), member.getEmail()); } + + public UserToken getToken(LoginRequest loginRequest) { + try { + Member member = memberDao.findByEmailAndPassword(loginRequest.email(), loginRequest.password()); + return jwtUtils.createToken(member); + } catch (EmptyResultDataAccessException e) { + throw new IllegalArgumentException("๋กœ๊ทธ์ธ ์ •๋ณด๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค."); + } + } + + public UserClaims checkLogin(UserToken userToken) { + try { + UserClaims userClaims = jwtUtils.getClaimsFromToken(userToken.token()); + Member member = memberDao.findByName(userClaims.name()); + return new UserClaims(member.getName()); + } catch (EmptyResultDataAccessException exception) { + throw new IllegalArgumentException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค."); + } + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a0f33bba..0e21f4ec 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,4 +8,4 @@ spring.datasource.url=jdbc:h2:mem:database #spring.jpa.ddl-auto=create-drop #spring.jpa.defer-datasource-initialization=true -#roomescape.auth.jwt.secret= Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E= \ No newline at end of file +roomescape.auth.jwt.secret= Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E= diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 6add784b..510711ae 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -32,7 +32,16 @@ public class MissionStepTest { .extract(); String token = response.headers().get("Set-Cookie").getValue().split(";")[0].split("=")[1]; - assertThat(token).isNotBlank(); + + ExtractableResponse checkResponse = RestAssured.given().log().all() + .contentType(ContentType.JSON) + .cookie("token", token) + .when().get("/login/check") + .then().log().all() + .statusCode(200) + .extract(); + + assertThat(checkResponse.body().jsonPath().getString("name")).isEqualTo("์–ด๋“œ๋ฏผ"); } -} \ No newline at end of file +} From 0186fcf56814ca90dc6feb26e5bf2f9dc233f9d2 Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Thu, 26 Dec 2024 17:27:26 +0900 Subject: [PATCH 03/13] =?UTF-8?q?[FEAT]=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20(2=EB=8B=A8=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++- src/test/java/roomescape/MissionStepTest.java | 60 +++++++++++++++---- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index c9355301..c5c53b12 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ #### ๋กœ๊ทธ์ธ ์š”์ฒญ + ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ -> ๋ฉค๋ฒ„ ์กฐํšŒ + ์กฐํšŒํ•œ ๋ฉค๋ฒ„๋กœ ํ† ํฐ ๋ฐœ๊ธ‰ - + Cookie ๋ฅผ ๋งŒ๋“ค์–ด ์‘๋‹ต + + Cookie๋ฅผ ๋งŒ๋“ค์–ด ์‘๋‹ต #### ์ธ์ฆ ์ •๋ณด ์กฐํšŒ + Cookie -> ํ† ํฐ ์ •๋ณด ์ถ”์ถœ + ๋ฉค๋ฒ„๋ฅผ ์ฐพ์•„์„œ ์‘๋‹ต @@ -16,3 +16,11 @@ + POST/login : ๋กœ๊ทธ์ธ ์š”์ฒญ + GET/login/check : ์ธ์ฆ ์ •๋ณด ์กฐํšŒ +## ๋กœ๊ทธ์ธ ๋ฆฌํŒฉํ„ฐ๋ง +#### HandlerMethodArgumentResolver ++ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ž๋™ ์ฃผ์ž… + +## ์˜ˆ์•ฝ ์ƒ์„ฑ ๊ธฐ๋Šฅ ๋ณ€๊ฒฝ ++ ์˜ˆ์•ฝ : ReservationRequest(์š”์ฒญ DTO) + -> name์ด ์žˆ์œผ๋ฉด name์œผ๋กœ Member ์ฐพ๊ธฐ + -> name์ด ์—†์œผ๋ฉด Cookie์— ๋‹ด๊ธด ์ •๋ณด ํ™œ์šฉ diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 510711ae..4288e25b 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import roomescape.reservation.ReservationResponse; import java.util.HashMap; import java.util.Map; @@ -19,29 +20,68 @@ public class MissionStepTest { @Test void ์ผ๋‹จ๊ณ„() { + String token = createToken("admin@email.com", "password"); + assertThat(token).isNotBlank(); + + ExtractableResponse checkResponse = RestAssured.given().log().all() + .contentType(ContentType.JSON) + .cookie("token", token) + .when().get("/login/check") + .then().log().all() + .statusCode(200) + .extract(); + + assertThat(checkResponse.body().jsonPath().getString("name")).isEqualTo("์–ด๋“œ๋ฏผ"); + } + + @Test + void ์ด๋‹จ๊ณ„() { + String token = createToken("admin@email.com", "password"); + Map params = new HashMap<>(); - params.put("email", "admin@email.com"); - params.put("password", "password"); + params.put("date", "2024-03-01"); + params.put("time", "1"); + params.put("theme", "1"); ExtractableResponse response = RestAssured.given().log().all() + .body(params) + .cookie("token", token) .contentType(ContentType.JSON) + .post("/reservations") + .then().log().all() + .extract(); + + assertThat(response.statusCode()).isEqualTo(201); + assertThat(response.as(ReservationResponse.class).getName()).isEqualTo("์–ด๋“œ๋ฏผ"); + + params.put("name", "๋ธŒ๋ผ์šด"); + + ExtractableResponse adminResponse = RestAssured.given().log().all() .body(params) - .when().post("/login") + .cookie("token", token) + .contentType(ContentType.JSON) + .post("/reservations") .then().log().all() - .statusCode(200) .extract(); - String token = response.headers().get("Set-Cookie").getValue().split(";")[0].split("=")[1]; - assertThat(token).isNotBlank(); + assertThat(adminResponse.statusCode()).isEqualTo(201); + assertThat(adminResponse.as(ReservationResponse.class).getName()).isEqualTo("๋ธŒ๋ผ์šด"); + } - ExtractableResponse checkResponse = RestAssured.given().log().all() + private String createToken(String email, String password) { + Map params = new HashMap<>(); + params.put("email", email); + params.put("password", password); + + ExtractableResponse response = RestAssured.given().log().all() .contentType(ContentType.JSON) - .cookie("token", token) - .when().get("/login/check") + .body(params) + .when().post("/login") .then().log().all() .statusCode(200) .extract(); - assertThat(checkResponse.body().jsonPath().getString("name")).isEqualTo("์–ด๋“œ๋ฏผ"); + String token = response.headers().get("Set-Cookie").getValue().split(";")[0].split("=")[1]; + return token; } } From 769b6bf0d2f7c49af5c58c4c7d1a159c9307120a Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Thu, 26 Dec 2024 20:27:20 +0900 Subject: [PATCH 04/13] =?UTF-8?q?[FEAT]=20ArgumentResolver=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20+=20=EC=98=88=EC=95=BD=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20(2=EB=8B=A8=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/auth/JWTUtils.java | 2 +- .../auth/LoginMemberArgumentResolver.java | 45 +++++++++++++++++++ src/main/java/roomescape/auth/UserClaims.java | 3 +- src/main/java/roomescape/auth/WebConfig.java | 22 +++++++++ .../java/roomescape/member/MemberService.java | 6 +-- .../reservation/ReservationController.java | 29 +++++++----- .../reservation/ReservationDao.java | 16 +++---- .../reservation/ReservationRequest.java | 27 +++-------- .../reservation/ReservationResponse.java | 41 +++-------------- .../reservation/ReservationService.java | 2 +- src/test/java/roomescape/MissionStepTest.java | 4 +- 11 files changed, 114 insertions(+), 83 deletions(-) create mode 100644 src/main/java/roomescape/auth/LoginMemberArgumentResolver.java create mode 100644 src/main/java/roomescape/auth/WebConfig.java diff --git a/src/main/java/roomescape/auth/JWTUtils.java b/src/main/java/roomescape/auth/JWTUtils.java index 9f710ab5..4a7ed24e 100644 --- a/src/main/java/roomescape/auth/JWTUtils.java +++ b/src/main/java/roomescape/auth/JWTUtils.java @@ -31,6 +31,6 @@ public UserClaims getClaimsFromToken(String token) { .parseClaimsJws(token) .getBody(); - return new UserClaims(claims.get("name", String.class)); + return new UserClaims(claims.get("name", String.class),claims.get("role", String.class)); } } diff --git a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java new file mode 100644 index 00000000..163f4443 --- /dev/null +++ b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java @@ -0,0 +1,45 @@ +package roomescape.auth; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.ServletWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import roomescape.member.MemberService; + +import java.util.Arrays; + +@Component +@RequiredArgsConstructor +public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { + + private final MemberService memberService; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.getParameterType().equals(UserClaims.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + HttpServletRequest request = ((ServletWebRequest) webRequest).getRequest(); + String token = extractToken(request); + + return memberService.checkLogin(new UserToken(token)); + } + + private String extractToken(HttpServletRequest request) { + return Arrays.stream(request.getCookies()) + .filter(cookie -> "token".equals(cookie.getName())) + .findFirst() + .map(Cookie::getValue) + .orElseThrow(()->new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + } + +} + diff --git a/src/main/java/roomescape/auth/UserClaims.java b/src/main/java/roomescape/auth/UserClaims.java index c2070c65..81270ee1 100644 --- a/src/main/java/roomescape/auth/UserClaims.java +++ b/src/main/java/roomescape/auth/UserClaims.java @@ -1,6 +1,7 @@ package roomescape.auth; public record UserClaims( - String name + String name, + String role ) { } diff --git a/src/main/java/roomescape/auth/WebConfig.java b/src/main/java/roomescape/auth/WebConfig.java new file mode 100644 index 00000000..8ee3d4fa --- /dev/null +++ b/src/main/java/roomescape/auth/WebConfig.java @@ -0,0 +1,22 @@ +package roomescape.auth; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + private final LoginMemberArgumentResolver loginMemberArgumentResolver; + + public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver) { + this.loginMemberArgumentResolver = loginMemberArgumentResolver; + } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(loginMemberArgumentResolver); + } +} diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index 089b6784..5270c51a 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -1,9 +1,7 @@ package roomescape.member; import lombok.RequiredArgsConstructor; -import org.springframework.dao.DataAccessException; import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import roomescape.auth.JWTUtils; import roomescape.auth.UserClaims; @@ -32,8 +30,8 @@ public UserToken getToken(LoginRequest loginRequest) { public UserClaims checkLogin(UserToken userToken) { try { UserClaims userClaims = jwtUtils.getClaimsFromToken(userToken.token()); - Member member = memberDao.findByName(userClaims.name()); - return new UserClaims(member.getName()); + memberDao.findByName(userClaims.name()); + return userClaims; } catch (EmptyResultDataAccessException exception) { throw new IllegalArgumentException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค."); } diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index b3bef399..ca4e87b1 100644 --- a/src/main/java/roomescape/reservation/ReservationController.java +++ b/src/main/java/roomescape/reservation/ReservationController.java @@ -1,5 +1,6 @@ package roomescape.reservation; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -7,35 +8,41 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import roomescape.auth.UserClaims; import java.net.URI; import java.util.List; @RestController +@RequiredArgsConstructor public class ReservationController { private final ReservationService reservationService; - public ReservationController(ReservationService reservationService) { - this.reservationService = reservationService; - } - @GetMapping("/reservations") public List list() { return reservationService.findAll(); } @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest reservationRequest) { - if (reservationRequest.getName() == null - || reservationRequest.getDate() == null - || reservationRequest.getTheme() == null - || reservationRequest.getTime() == null) { + public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, UserClaims userClaims) { + if (reservationRequest.date() == null + || reservationRequest.theme() == null + || reservationRequest.time() == null) { return ResponseEntity.badRequest().build(); } - ReservationResponse reservation = reservationService.save(reservationRequest); - return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation); + if (reservationRequest.name() == null) { + reservationRequest = new ReservationRequest( + userClaims.name(), + reservationRequest.date(), + reservationRequest.theme(), + reservationRequest.time() + ); + } + + ReservationResponse reservation = reservationService.save(reservationRequest); + return ResponseEntity.created(URI.create("/reservations/" + reservation.id())).body(reservation); } @DeleteMapping("/reservations/{id}") diff --git a/src/main/java/roomescape/reservation/ReservationDao.java b/src/main/java/roomescape/reservation/ReservationDao.java index a4972430..d75201e3 100644 --- a/src/main/java/roomescape/reservation/ReservationDao.java +++ b/src/main/java/roomescape/reservation/ReservationDao.java @@ -47,25 +47,25 @@ public Reservation save(ReservationRequest reservationRequest) { KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(connection -> { PreparedStatement ps = connection.prepareStatement("INSERT INTO reservation(date, name, theme_id, time_id) VALUES (?, ?, ?, ?)", new String[]{"id"}); - ps.setString(1, reservationRequest.getDate()); - ps.setString(2, reservationRequest.getName()); - ps.setLong(3, reservationRequest.getTheme()); - ps.setLong(4, reservationRequest.getTime()); + ps.setString(1, reservationRequest.date()); + ps.setString(2, reservationRequest.name()); + ps.setLong(3, reservationRequest.theme()); + ps.setLong(4, reservationRequest.time()); return ps; }, keyHolder); Time time = jdbcTemplate.queryForObject("SELECT * FROM time WHERE id = ?", (rs, rowNum) -> new Time(rs.getLong("id"), rs.getString("time_value")), - reservationRequest.getTime()); + reservationRequest.time()); Theme theme = jdbcTemplate.queryForObject("SELECT * FROM theme WHERE id = ?", (rs, rowNum) -> new Theme(rs.getLong("id"), rs.getString("name"), rs.getString("description")), - reservationRequest.getTheme()); + reservationRequest.theme()); return new Reservation( keyHolder.getKey().longValue(), - reservationRequest.getName(), - reservationRequest.getDate(), + reservationRequest.name(), + reservationRequest.date(), time, theme ); diff --git a/src/main/java/roomescape/reservation/ReservationRequest.java b/src/main/java/roomescape/reservation/ReservationRequest.java index 19f44124..ed0d50b0 100644 --- a/src/main/java/roomescape/reservation/ReservationRequest.java +++ b/src/main/java/roomescape/reservation/ReservationRequest.java @@ -1,24 +1,9 @@ package roomescape.reservation; -public class ReservationRequest { - private String name; - private String date; - private Long theme; - private Long time; - - public String getName() { - return name; - } - - public String getDate() { - return date; - } - - public Long getTheme() { - return theme; - } - - public Long getTime() { - return time; - } +public record ReservationRequest ( + String name, + String date, + Long theme, + Long time +){ } diff --git a/src/main/java/roomescape/reservation/ReservationResponse.java b/src/main/java/roomescape/reservation/ReservationResponse.java index 41360a36..be7cb163 100644 --- a/src/main/java/roomescape/reservation/ReservationResponse.java +++ b/src/main/java/roomescape/reservation/ReservationResponse.java @@ -1,37 +1,10 @@ package roomescape.reservation; -public class ReservationResponse { - private Long id; - private String name; - private String theme; - private String date; - private String time; - - public ReservationResponse(Long id, String name, String theme, String date, String time) { - this.id = id; - this.name = name; - this.theme = theme; - this.date = date; - this.time = time; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getTheme() { - return theme; - } - - public String getDate() { - return date; - } - - public String getTime() { - return time; - } +public record ReservationResponse( + Long id, + String name, + String date, + String theme, + String time +){ } diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java index bd331332..0350dc30 100644 --- a/src/main/java/roomescape/reservation/ReservationService.java +++ b/src/main/java/roomescape/reservation/ReservationService.java @@ -15,7 +15,7 @@ public ReservationService(ReservationDao reservationDao) { public ReservationResponse save(ReservationRequest reservationRequest) { Reservation reservation = reservationDao.save(reservationRequest); - return new ReservationResponse(reservation.getId(), reservationRequest.getName(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); + return new ReservationResponse(reservation.getId(), reservationRequest.name(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); } public void deleteById(Long id) { diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 4288e25b..98eebea1 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -52,7 +52,7 @@ public class MissionStepTest { .extract(); assertThat(response.statusCode()).isEqualTo(201); - assertThat(response.as(ReservationResponse.class).getName()).isEqualTo("์–ด๋“œ๋ฏผ"); + assertThat(response.as(ReservationResponse.class).name()).isEqualTo("์–ด๋“œ๋ฏผ"); params.put("name", "๋ธŒ๋ผ์šด"); @@ -65,7 +65,7 @@ public class MissionStepTest { .extract(); assertThat(adminResponse.statusCode()).isEqualTo(201); - assertThat(adminResponse.as(ReservationResponse.class).getName()).isEqualTo("๋ธŒ๋ผ์šด"); + assertThat(adminResponse.as(ReservationResponse.class).name()).isEqualTo("๋ธŒ๋ผ์šด"); } private String createToken(String email, String password) { From 9b180fb325e7b373c8e5aeefcbeb579e71680d99 Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Thu, 26 Dec 2024 20:34:54 +0900 Subject: [PATCH 05/13] =?UTF-8?q?[FEAT]=20=EB=A6=AC=EB=93=9C=EB=AF=B8=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20(3=EB=8B=A8=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 11 +++++++++++ src/test/java/roomescape/MissionStepTest.java | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/README.md b/README.md index c5c53b12..eef270e3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # ๐ŸŒ€ Spring MVC (์ธ์ฆ) +# 1๋‹จ๊ณ„ +___ ## ๋กœ๊ทธ์ธ #### ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ + ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ @@ -16,6 +18,8 @@ + POST/login : ๋กœ๊ทธ์ธ ์š”์ฒญ + GET/login/check : ์ธ์ฆ ์ •๋ณด ์กฐํšŒ +# 2๋‹จ๊ณ„ +___ ## ๋กœ๊ทธ์ธ ๋ฆฌํŒฉํ„ฐ๋ง #### HandlerMethodArgumentResolver + ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ž๋™ ์ฃผ์ž… @@ -24,3 +28,10 @@ + ์˜ˆ์•ฝ : ReservationRequest(์š”์ฒญ DTO) -> name์ด ์žˆ์œผ๋ฉด name์œผ๋กœ Member ์ฐพ๊ธฐ -> name์ด ์—†์œผ๋ฉด Cookie์— ๋‹ด๊ธด ์ •๋ณด ํ™œ์šฉ + +# 3๋‹จ๊ณ„ +___ +## ๊ด€๋ฆฌ์ž ๊ธฐ๋Šฅ ++ admin ํŽ˜์ด์ง€ ์ง„์ž… (HandlerInterceptor ์ด์šฉ) + -> ๊ด€๋ฆฌ์ž : ์ง„์ž… ๊ฐ€๋Šฅ + -> ๊ด€๋ฆฌ์ž X : 401 ์ฝ”๋“œ ์‘๋‹ต diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 98eebea1..d2444c6c 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -68,6 +68,25 @@ public class MissionStepTest { assertThat(adminResponse.as(ReservationResponse.class).name()).isEqualTo("๋ธŒ๋ผ์šด"); } + @Test + void ์‚ผ๋‹จ๊ณ„() { + String brownToken = createToken("brown@email.com", "password"); + + RestAssured.given().log().all() + .cookie("token", brownToken) + .get("/admin") + .then().log().all() + .statusCode(401); + + String adminToken = createToken("admin@email.com", "password"); + + RestAssured.given().log().all() + .cookie("token", adminToken) + .get("/admin") + .then().log().all() + .statusCode(200); + } + private String createToken(String email, String password) { Map params = new HashMap<>(); params.put("email", email); From 2ebef371f7eef15f2e7a88e39300e9f022e0f56e Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Fri, 27 Dec 2024 00:56:47 +0900 Subject: [PATCH 06/13] =?UTF-8?q?[FEAT]=20=EC=96=B4=EB=93=9C=EB=AF=BC=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B6=8C=ED=95=9C=20(3=EB=8B=A8?= =?UTF-8?q?=EA=B3=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/LoginMemberArgumentResolver.java | 11 ++----- .../roomescape/auth/RoleCheckInterceptor.java | 33 +++++++++++++++++++ src/main/java/roomescape/auth/WebConfig.java | 13 +++++--- 3 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 src/main/java/roomescape/auth/RoleCheckInterceptor.java diff --git a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java index 163f4443..77d78d78 100644 --- a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java +++ b/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java @@ -28,18 +28,13 @@ public boolean supportsParameter(MethodParameter parameter) { @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = ((ServletWebRequest) webRequest).getRequest(); - String token = extractToken(request); - - return memberService.checkLogin(new UserToken(token)); - } - - private String extractToken(HttpServletRequest request) { - return Arrays.stream(request.getCookies()) + String token = Arrays.stream(request.getCookies()) .filter(cookie -> "token".equals(cookie.getName())) .findFirst() .map(Cookie::getValue) .orElseThrow(()->new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); - } + return memberService.checkLogin(new UserToken(token)); + } } diff --git a/src/main/java/roomescape/auth/RoleCheckInterceptor.java b/src/main/java/roomescape/auth/RoleCheckInterceptor.java new file mode 100644 index 00000000..5a75ccac --- /dev/null +++ b/src/main/java/roomescape/auth/RoleCheckInterceptor.java @@ -0,0 +1,33 @@ +package roomescape.auth; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import roomescape.member.MemberService; + +import java.util.Arrays; + +@Component +@RequiredArgsConstructor +public class RoleCheckInterceptor implements HandlerInterceptor { + private final MemberService memberService; + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String token = Arrays.stream(request.getCookies()) + .filter(cookie -> "token".equals(cookie.getName())) + .findFirst() + .map(Cookie::getValue) + .orElseThrow(()->new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + + UserClaims userClaims = memberService.checkLogin(new UserToken(token)); + if (!userClaims.role().equals("ADMIN")) { + response.setStatus(401); + return false; + } + + return true; + } +} diff --git a/src/main/java/roomescape/auth/WebConfig.java b/src/main/java/roomescape/auth/WebConfig.java index 8ee3d4fa..09d76a1a 100644 --- a/src/main/java/roomescape/auth/WebConfig.java +++ b/src/main/java/roomescape/auth/WebConfig.java @@ -1,22 +1,27 @@ package roomescape.auth; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @Configuration +@RequiredArgsConstructor public class WebConfig implements WebMvcConfigurer { private final LoginMemberArgumentResolver loginMemberArgumentResolver; - - public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver) { - this.loginMemberArgumentResolver = loginMemberArgumentResolver; - } + private final RoleCheckInterceptor roleCheckInterceptor; @Override public void addArgumentResolvers(List resolvers) { resolvers.add(loginMemberArgumentResolver); } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(roleCheckInterceptor).addPathPatterns("/admin/**"); + } } From f9c1a7dcf57a908a602149f4c9bfa61cddb353d8 Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Fri, 27 Dec 2024 01:30:23 +0900 Subject: [PATCH 07/13] =?UTF-8?q?[REFACTOR]=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EB=AA=85=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/{UserClaims.java => AuthClaims.java} | 2 +- ...r.java => AuthClaimsArgumentResolver.java} | 8 +++--- ...erceptor.java => AuthRoleInterceptor.java} | 7 ++--- .../auth/{UserToken.java => AuthToken.java} | 2 +- .../{WebConfig.java => AuthWebConfig.java} | 6 ++--- src/main/java/roomescape/auth/JWTUtils.java | 8 +++--- .../roomescape/member/MemberController.java | 22 +++++++-------- .../java/roomescape/member/MemberRequest.java | 21 +++++---------- .../roomescape/member/MemberResponse.java | 27 ++++--------------- .../java/roomescape/member/MemberService.java | 12 ++++----- .../reservation/ReservationController.java | 4 +-- 11 files changed, 45 insertions(+), 74 deletions(-) rename src/main/java/roomescape/auth/{UserClaims.java => AuthClaims.java} (73%) rename src/main/java/roomescape/auth/{LoginMemberArgumentResolver.java => AuthClaimsArgumentResolver.java} (80%) rename src/main/java/roomescape/auth/{RoleCheckInterceptor.java => AuthRoleInterceptor.java} (79%) rename src/main/java/roomescape/auth/{UserToken.java => AuthToken.java} (67%) rename src/main/java/roomescape/auth/{WebConfig.java => AuthWebConfig.java} (79%) diff --git a/src/main/java/roomescape/auth/UserClaims.java b/src/main/java/roomescape/auth/AuthClaims.java similarity index 73% rename from src/main/java/roomescape/auth/UserClaims.java rename to src/main/java/roomescape/auth/AuthClaims.java index 81270ee1..2c98324e 100644 --- a/src/main/java/roomescape/auth/UserClaims.java +++ b/src/main/java/roomescape/auth/AuthClaims.java @@ -1,6 +1,6 @@ package roomescape.auth; -public record UserClaims( +public record AuthClaims( String name, String role ) { diff --git a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java b/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java similarity index 80% rename from src/main/java/roomescape/auth/LoginMemberArgumentResolver.java rename to src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java index 77d78d78..7026488e 100644 --- a/src/main/java/roomescape/auth/LoginMemberArgumentResolver.java +++ b/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java @@ -16,13 +16,13 @@ @Component @RequiredArgsConstructor -public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { +public class AuthClaimsArgumentResolver implements HandlerMethodArgumentResolver { private final MemberService memberService; @Override public boolean supportsParameter(MethodParameter parameter) { - return parameter.getParameterType().equals(UserClaims.class); + return parameter.getParameterType().equals(AuthClaims.class); } @Override @@ -32,9 +32,9 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m .filter(cookie -> "token".equals(cookie.getName())) .findFirst() .map(Cookie::getValue) - .orElseThrow(()->new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + .orElseThrow(() -> new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); - return memberService.checkLogin(new UserToken(token)); + return memberService.checkLogin(new AuthToken(token)); } } diff --git a/src/main/java/roomescape/auth/RoleCheckInterceptor.java b/src/main/java/roomescape/auth/AuthRoleInterceptor.java similarity index 79% rename from src/main/java/roomescape/auth/RoleCheckInterceptor.java rename to src/main/java/roomescape/auth/AuthRoleInterceptor.java index 5a75ccac..7c963d7e 100644 --- a/src/main/java/roomescape/auth/RoleCheckInterceptor.java +++ b/src/main/java/roomescape/auth/AuthRoleInterceptor.java @@ -12,17 +12,18 @@ @Component @RequiredArgsConstructor -public class RoleCheckInterceptor implements HandlerInterceptor { +public class AuthRoleInterceptor implements HandlerInterceptor { private final MemberService memberService; + @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = Arrays.stream(request.getCookies()) .filter(cookie -> "token".equals(cookie.getName())) .findFirst() .map(Cookie::getValue) - .orElseThrow(()->new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + .orElseThrow(() -> new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); - UserClaims userClaims = memberService.checkLogin(new UserToken(token)); + AuthClaims userClaims = memberService.checkLogin(new AuthToken(token)); if (!userClaims.role().equals("ADMIN")) { response.setStatus(401); return false; diff --git a/src/main/java/roomescape/auth/UserToken.java b/src/main/java/roomescape/auth/AuthToken.java similarity index 67% rename from src/main/java/roomescape/auth/UserToken.java rename to src/main/java/roomescape/auth/AuthToken.java index ce2beb5b..183c507b 100644 --- a/src/main/java/roomescape/auth/UserToken.java +++ b/src/main/java/roomescape/auth/AuthToken.java @@ -1,6 +1,6 @@ package roomescape.auth; -public record UserToken( +public record AuthToken( String token ) { } diff --git a/src/main/java/roomescape/auth/WebConfig.java b/src/main/java/roomescape/auth/AuthWebConfig.java similarity index 79% rename from src/main/java/roomescape/auth/WebConfig.java rename to src/main/java/roomescape/auth/AuthWebConfig.java index 09d76a1a..7c35c920 100644 --- a/src/main/java/roomescape/auth/WebConfig.java +++ b/src/main/java/roomescape/auth/AuthWebConfig.java @@ -10,10 +10,10 @@ @Configuration @RequiredArgsConstructor -public class WebConfig implements WebMvcConfigurer { +public class AuthWebConfig implements WebMvcConfigurer { - private final LoginMemberArgumentResolver loginMemberArgumentResolver; - private final RoleCheckInterceptor roleCheckInterceptor; + private final AuthClaimsArgumentResolver loginMemberArgumentResolver; + private final AuthRoleInterceptor roleCheckInterceptor; @Override public void addArgumentResolvers(List resolvers) { diff --git a/src/main/java/roomescape/auth/JWTUtils.java b/src/main/java/roomescape/auth/JWTUtils.java index 4a7ed24e..60b92f1e 100644 --- a/src/main/java/roomescape/auth/JWTUtils.java +++ b/src/main/java/roomescape/auth/JWTUtils.java @@ -13,7 +13,7 @@ public class JWTUtils { @Value("${roomescape.auth.jwt.secret}") private String secretKey; - public UserToken createToken(Member member){ + public AuthToken createToken(Member member) { String accessToken = Jwts.builder() .setSubject(member.getId().toString()) .claim("name", member.getName()) @@ -21,16 +21,16 @@ public UserToken createToken(Member member){ .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) .compact(); - return new UserToken(accessToken); + return new AuthToken(accessToken); } - public UserClaims getClaimsFromToken(String token) { + public AuthClaims getClaimsFromToken(String token) { Claims claims = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) .build() .parseClaimsJws(token) .getBody(); - return new UserClaims(claims.get("name", String.class),claims.get("role", String.class)); + return new AuthClaims(claims.get("name", String.class), claims.get("role", String.class)); } } diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 19dee8e6..d05ec3ca 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -1,36 +1,32 @@ package roomescape.member; import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServlet; -import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import roomescape.auth.UserClaims; -import roomescape.auth.UserToken; +import roomescape.auth.AuthClaims; +import roomescape.auth.AuthToken; import java.net.URI; @RestController +@RequiredArgsConstructor public class MemberController { - private MemberService memberService; - - public MemberController(MemberService memberService) { - this.memberService = memberService; - } + private final MemberService memberService; @PostMapping("/members") public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { MemberResponse member = memberService.createMember(memberRequest); - return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member); + return ResponseEntity.created(URI.create("/members/" + member.id())).body(member); } @PostMapping("/login") - public void login(@RequestBody LoginRequest loginRequest, HttpServletResponse response){ + public void login(@RequestBody LoginRequest loginRequest, HttpServletResponse response) { String token = memberService.getToken(loginRequest).token(); Cookie cookie = new Cookie("token", token); cookie.setHttpOnly(true); @@ -39,8 +35,8 @@ public void login(@RequestBody LoginRequest loginRequest, HttpServletResponse re } @GetMapping("/login/check") - public UserClaims checkLogin(@CookieValue("token") String token){ - return memberService.checkLogin(new UserToken(token)); + public AuthClaims checkLogin(@CookieValue("token") String token) { + return memberService.checkLogin(new AuthToken(token)); } @PostMapping("/logout") diff --git a/src/main/java/roomescape/member/MemberRequest.java b/src/main/java/roomescape/member/MemberRequest.java index cafb79f1..8d892b82 100644 --- a/src/main/java/roomescape/member/MemberRequest.java +++ b/src/main/java/roomescape/member/MemberRequest.java @@ -1,19 +1,10 @@ package roomescape.member; -public class MemberRequest { - private String name; - private String email; - private String password; - public String getName() { - return name; - } - - public String getEmail() { - return email; - } - - public String getPassword() { - return password; - } +public record MemberRequest( + String name, + String email, + String password +) { } + diff --git a/src/main/java/roomescape/member/MemberResponse.java b/src/main/java/roomescape/member/MemberResponse.java index b9fa3b97..f2408ba3 100644 --- a/src/main/java/roomescape/member/MemberResponse.java +++ b/src/main/java/roomescape/member/MemberResponse.java @@ -1,25 +1,8 @@ package roomescape.member; -public class MemberResponse { - private Long id; - private String name; - private String email; - - public MemberResponse(Long id, String name, String email) { - this.id = id; - this.name = name; - this.email = email; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getEmail() { - return email; - } +public record MemberResponse( + Long id, + String name, + String email +) { } diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index 5270c51a..48b899b9 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -3,9 +3,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.stereotype.Service; +import roomescape.auth.AuthClaims; +import roomescape.auth.AuthToken; import roomescape.auth.JWTUtils; -import roomescape.auth.UserClaims; -import roomescape.auth.UserToken; @Service @RequiredArgsConstructor @@ -14,11 +14,11 @@ public class MemberService { private final JWTUtils jwtUtils; public MemberResponse createMember(MemberRequest memberRequest) { - Member member = memberDao.save(new Member(memberRequest.getName(), memberRequest.getEmail(), memberRequest.getPassword(), "USER")); + Member member = memberDao.save(new Member(memberRequest.name(), memberRequest.email(), memberRequest.password(), "USER")); return new MemberResponse(member.getId(), member.getName(), member.getEmail()); } - public UserToken getToken(LoginRequest loginRequest) { + public AuthToken getToken(LoginRequest loginRequest) { try { Member member = memberDao.findByEmailAndPassword(loginRequest.email(), loginRequest.password()); return jwtUtils.createToken(member); @@ -27,9 +27,9 @@ public UserToken getToken(LoginRequest loginRequest) { } } - public UserClaims checkLogin(UserToken userToken) { + public AuthClaims checkLogin(AuthToken userToken) { try { - UserClaims userClaims = jwtUtils.getClaimsFromToken(userToken.token()); + AuthClaims userClaims = jwtUtils.getClaimsFromToken(userToken.token()); memberDao.findByName(userClaims.name()); return userClaims; } catch (EmptyResultDataAccessException exception) { diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index ca4e87b1..e5ccecf2 100644 --- a/src/main/java/roomescape/reservation/ReservationController.java +++ b/src/main/java/roomescape/reservation/ReservationController.java @@ -8,7 +8,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import roomescape.auth.UserClaims; +import roomescape.auth.AuthClaims; import java.net.URI; import java.util.List; @@ -25,7 +25,7 @@ public List list() { } @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, UserClaims userClaims) { + public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, AuthClaims userClaims) { if (reservationRequest.date() == null || reservationRequest.theme() == null || reservationRequest.time() == null) { From 2e0884e8208b9887c507fe2904d2ddc805f5196d Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:48:44 +0900 Subject: [PATCH 08/13] =?UTF-8?q?[REFACTOR]=20AuthClaims=20=EC=99=80=20Log?= =?UTF-8?q?inResponse=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/roomescape/auth/AuthClaimsArgumentResolver.java | 5 ++--- src/main/java/roomescape/auth/AuthRoleInterceptor.java | 8 ++++---- src/main/java/roomescape/member/LoginResponse.java | 6 ++++++ src/main/java/roomescape/member/MemberController.java | 3 +-- src/main/java/roomescape/member/MemberService.java | 6 +++--- 5 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 src/main/java/roomescape/member/LoginResponse.java diff --git a/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java b/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java index 7026488e..d57f1cbb 100644 --- a/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java +++ b/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java @@ -10,7 +10,6 @@ import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; -import roomescape.member.MemberService; import java.util.Arrays; @@ -18,7 +17,7 @@ @RequiredArgsConstructor public class AuthClaimsArgumentResolver implements HandlerMethodArgumentResolver { - private final MemberService memberService; + private final JWTUtils jwtUtils; @Override public boolean supportsParameter(MethodParameter parameter) { @@ -34,7 +33,7 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m .map(Cookie::getValue) .orElseThrow(() -> new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); - return memberService.checkLogin(new AuthToken(token)); + return jwtUtils.getClaimsFromToken(token); } } diff --git a/src/main/java/roomescape/auth/AuthRoleInterceptor.java b/src/main/java/roomescape/auth/AuthRoleInterceptor.java index 7c963d7e..fe81fc2c 100644 --- a/src/main/java/roomescape/auth/AuthRoleInterceptor.java +++ b/src/main/java/roomescape/auth/AuthRoleInterceptor.java @@ -6,14 +6,14 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; -import roomescape.member.MemberService; import java.util.Arrays; @Component @RequiredArgsConstructor public class AuthRoleInterceptor implements HandlerInterceptor { - private final MemberService memberService; + + private final JWTUtils jwtUtils; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { @@ -23,8 +23,8 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons .map(Cookie::getValue) .orElseThrow(() -> new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); - AuthClaims userClaims = memberService.checkLogin(new AuthToken(token)); - if (!userClaims.role().equals("ADMIN")) { + + if (!jwtUtils.getClaimsFromToken(token).role().equals("ADMIN")) { response.setStatus(401); return false; } diff --git a/src/main/java/roomescape/member/LoginResponse.java b/src/main/java/roomescape/member/LoginResponse.java new file mode 100644 index 00000000..1dd241d4 --- /dev/null +++ b/src/main/java/roomescape/member/LoginResponse.java @@ -0,0 +1,6 @@ +package roomescape.member; + +public record LoginResponse ( + String name +){ +} diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index d05ec3ca..0682ba0c 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -9,7 +9,6 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import roomescape.auth.AuthClaims; import roomescape.auth.AuthToken; import java.net.URI; @@ -35,7 +34,7 @@ public void login(@RequestBody LoginRequest loginRequest, HttpServletResponse re } @GetMapping("/login/check") - public AuthClaims checkLogin(@CookieValue("token") String token) { + public LoginResponse checkLogin(@CookieValue("token") String token) { return memberService.checkLogin(new AuthToken(token)); } diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index 48b899b9..83293ccf 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -27,11 +27,11 @@ public AuthToken getToken(LoginRequest loginRequest) { } } - public AuthClaims checkLogin(AuthToken userToken) { + public LoginResponse checkLogin(AuthToken userToken) { try { AuthClaims userClaims = jwtUtils.getClaimsFromToken(userToken.token()); - memberDao.findByName(userClaims.name()); - return userClaims; + Member member = memberDao.findByName(userClaims.name()); + return new LoginResponse(member.getName()); } catch (EmptyResultDataAccessException exception) { throw new IllegalArgumentException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค."); } From 0cfc7eb406e124d78fc7fef0111112637ead2064 Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:59:01 +0900 Subject: [PATCH 09/13] =?UTF-8?q?[REFACTOR]=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=B6=94=EC=B6=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/AuthClaimsArgumentResolver.java | 9 +-------- .../java/roomescape/auth/AuthRoleInterceptor.java | 10 +--------- src/main/java/roomescape/auth/JWTUtils.java | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java b/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java index d57f1cbb..7c93602b 100644 --- a/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java +++ b/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java @@ -1,6 +1,5 @@ package roomescape.auth; -import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.core.MethodParameter; @@ -11,8 +10,6 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; -import java.util.Arrays; - @Component @RequiredArgsConstructor public class AuthClaimsArgumentResolver implements HandlerMethodArgumentResolver { @@ -27,11 +24,7 @@ public boolean supportsParameter(MethodParameter parameter) { @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = ((ServletWebRequest) webRequest).getRequest(); - String token = Arrays.stream(request.getCookies()) - .filter(cookie -> "token".equals(cookie.getName())) - .findFirst() - .map(Cookie::getValue) - .orElseThrow(() -> new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + String token = jwtUtils.extractToken(request).token(); return jwtUtils.getClaimsFromToken(token); } diff --git a/src/main/java/roomescape/auth/AuthRoleInterceptor.java b/src/main/java/roomescape/auth/AuthRoleInterceptor.java index fe81fc2c..a7a70df1 100644 --- a/src/main/java/roomescape/auth/AuthRoleInterceptor.java +++ b/src/main/java/roomescape/auth/AuthRoleInterceptor.java @@ -1,14 +1,11 @@ package roomescape.auth; -import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; -import java.util.Arrays; - @Component @RequiredArgsConstructor public class AuthRoleInterceptor implements HandlerInterceptor { @@ -17,12 +14,7 @@ public class AuthRoleInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - String token = Arrays.stream(request.getCookies()) - .filter(cookie -> "token".equals(cookie.getName())) - .findFirst() - .map(Cookie::getValue) - .orElseThrow(() -> new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); - + String token = jwtUtils.extractToken(request).token(); if (!jwtUtils.getClaimsFromToken(token).role().equals("ADMIN")) { response.setStatus(401); diff --git a/src/main/java/roomescape/auth/JWTUtils.java b/src/main/java/roomescape/auth/JWTUtils.java index 60b92f1e..ea0fc91a 100644 --- a/src/main/java/roomescape/auth/JWTUtils.java +++ b/src/main/java/roomescape/auth/JWTUtils.java @@ -3,10 +3,14 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import roomescape.member.Member; +import java.util.Arrays; + @Component public class JWTUtils { @@ -24,6 +28,16 @@ public AuthToken createToken(Member member) { return new AuthToken(accessToken); } + public AuthToken extractToken(HttpServletRequest request){ + String token = Arrays.stream(request.getCookies()) + .filter(cookie -> "token".equals(cookie.getName())) + .findFirst() + .map(Cookie::getValue) + .orElseThrow(() -> new IllegalArgumentException("ํ† ํฐ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")); + + return new AuthToken(token); + } + public AuthClaims getClaimsFromToken(String token) { Claims claims = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) From ff3ad6a50ee9b53776542e6c289064ba995a9774 Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:33:50 +0900 Subject: [PATCH 10/13] =?UTF-8?q?[REFACTOR]=20Role=20enum=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/member/Member.java | 8 ++++---- src/main/java/roomescape/member/Role.java | 7 +++++++ 2 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 src/main/java/roomescape/member/Role.java diff --git a/src/main/java/roomescape/member/Member.java b/src/main/java/roomescape/member/Member.java index 903aaa9b..59103cee 100644 --- a/src/main/java/roomescape/member/Member.java +++ b/src/main/java/roomescape/member/Member.java @@ -5,20 +5,20 @@ public class Member { private String name; private String email; private String password; - private String role; + private Role role; public Member(Long id, String name, String email, String role) { this.id = id; this.name = name; this.email = email; - this.role = role; + this.role = Role.valueOf(role); } public Member(String name, String email, String password, String role) { this.name = name; this.email = email; this.password = password; - this.role = role; + this.role = Role.valueOf(role); } public Long getId() { @@ -38,6 +38,6 @@ public String getPassword() { } public String getRole() { - return role; + return role.name(); } } diff --git a/src/main/java/roomescape/member/Role.java b/src/main/java/roomescape/member/Role.java new file mode 100644 index 00000000..b23176b7 --- /dev/null +++ b/src/main/java/roomescape/member/Role.java @@ -0,0 +1,7 @@ +package roomescape.member; + +public enum Role { + ADMIN, + USER + +} From 75132bc3aeccfd7c18bcc5b4be743a01c2219d9a Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Mon, 13 Jan 2025 20:03:15 +0900 Subject: [PATCH 11/13] =?UTF-8?q?[REFACTOR]=20=EC=98=88=EC=99=B8=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=ED=95=B8=EB=93=A4=EB=9F=AC=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/roomescape/ExceptionController.java | 14 ------- src/main/java/roomescape/auth/JWTUtils.java | 31 ++++++++++++--- .../exception/ExceptionController.java | 39 +++++++++++++++++++ .../exception/InvalidLoginException.java | 7 ++++ .../exception/InvalidTokenException.java | 7 ++++ .../exception/MemberNotFoundException.java | 7 ++++ .../exception/MissingTokenException.java | 7 ++++ .../exception/RoomescapeException.java | 7 ++++ 8 files changed, 99 insertions(+), 20 deletions(-) delete mode 100644 src/main/java/roomescape/ExceptionController.java create mode 100644 src/main/java/roomescape/exception/ExceptionController.java create mode 100644 src/main/java/roomescape/exception/InvalidLoginException.java create mode 100644 src/main/java/roomescape/exception/InvalidTokenException.java create mode 100644 src/main/java/roomescape/exception/MemberNotFoundException.java create mode 100644 src/main/java/roomescape/exception/MissingTokenException.java create mode 100644 src/main/java/roomescape/exception/RoomescapeException.java diff --git a/src/main/java/roomescape/ExceptionController.java b/src/main/java/roomescape/ExceptionController.java deleted file mode 100644 index 4e2450f9..00000000 --- a/src/main/java/roomescape/ExceptionController.java +++ /dev/null @@ -1,14 +0,0 @@ -package roomescape; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; - -@ControllerAdvice -public class ExceptionController { - @ExceptionHandler(Exception.class) - public ResponseEntity handleRuntimeException(Exception e) { - e.printStackTrace(); - return ResponseEntity.badRequest().build(); - } -} diff --git a/src/main/java/roomescape/auth/JWTUtils.java b/src/main/java/roomescape/auth/JWTUtils.java index ea0fc91a..d9ccf03b 100644 --- a/src/main/java/roomescape/auth/JWTUtils.java +++ b/src/main/java/roomescape/auth/JWTUtils.java @@ -1,12 +1,15 @@ package roomescape.auth; import io.jsonwebtoken.Claims; +import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import roomescape.exception.InvalidTokenException; +import roomescape.exception.MissingTokenException; import roomescape.member.Member; import java.util.Arrays; @@ -39,12 +42,28 @@ public AuthToken extractToken(HttpServletRequest request){ } public AuthClaims getClaimsFromToken(String token) { - Claims claims = Jwts.parserBuilder() - .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) - .build() - .parseClaimsJws(token) - .getBody(); + if (token == null || token.isBlank()) { + throw new MissingTokenException("ํ† ํฐ์ด ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค."); + } - return new AuthClaims(claims.get("name", String.class), claims.get("role", String.class)); + try { + Claims claims = Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) + .build() + .parseClaimsJws(token) + .getBody(); + + String name = claims.get("name", String.class); + String role = claims.get("role", String.class); + + if (name == null || role == null) { + throw new InvalidTokenException("ํ•„์ˆ˜ ํด๋ ˆ์ž„์ด ๋ˆ„๋ฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."); + } + + return new AuthClaims(name, role); + + } catch (JwtException e) { + throw new InvalidTokenException("์œ ํšจํ•˜์ง€ ์•Š์€ ํ† ํฐ์ž…๋‹ˆ๋‹ค."); + } } } diff --git a/src/main/java/roomescape/exception/ExceptionController.java b/src/main/java/roomescape/exception/ExceptionController.java new file mode 100644 index 00000000..2d924653 --- /dev/null +++ b/src/main/java/roomescape/exception/ExceptionController.java @@ -0,0 +1,39 @@ +package roomescape.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class ExceptionController { + @ExceptionHandler(InvalidTokenException.class) + public ResponseEntity handleInvalidTokenException(InvalidLoginException e) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage()); + } + + @ExceptionHandler(MissingTokenException.class) + public ResponseEntity handleMissingTokenException(InvalidLoginException e) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage()); + } + + @ExceptionHandler(InvalidLoginException.class) + public ResponseEntity handleInvalidLoginException(InvalidLoginException e) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); + } + + @ExceptionHandler(MemberNotFoundException.class) + public ResponseEntity handleMemberNotFoundException(MemberNotFoundException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleException(Exception e) { + return ResponseEntity.internalServerError().body("์˜๋„๋˜์ง€ ์•Š์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค"); + } + + @ExceptionHandler(RoomescapeException.class) + public ResponseEntity handleExceptions(RoomescapeException e) { + return ResponseEntity.internalServerError().body("ํ”„๋กœ๊ทธ๋žจ ๋‚ด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."); + } +} diff --git a/src/main/java/roomescape/exception/InvalidLoginException.java b/src/main/java/roomescape/exception/InvalidLoginException.java new file mode 100644 index 00000000..ef9ac50a --- /dev/null +++ b/src/main/java/roomescape/exception/InvalidLoginException.java @@ -0,0 +1,7 @@ +package roomescape.exception; + +public class InvalidLoginException extends RoomescapeException { + public InvalidLoginException(String message) { + super(message); + } +} diff --git a/src/main/java/roomescape/exception/InvalidTokenException.java b/src/main/java/roomescape/exception/InvalidTokenException.java new file mode 100644 index 00000000..bd1903da --- /dev/null +++ b/src/main/java/roomescape/exception/InvalidTokenException.java @@ -0,0 +1,7 @@ +package roomescape.exception; + +public class InvalidTokenException extends RoomescapeException{ + public InvalidTokenException(String message) { + super(message); + } +} diff --git a/src/main/java/roomescape/exception/MemberNotFoundException.java b/src/main/java/roomescape/exception/MemberNotFoundException.java new file mode 100644 index 00000000..3e6a504f --- /dev/null +++ b/src/main/java/roomescape/exception/MemberNotFoundException.java @@ -0,0 +1,7 @@ +package roomescape.exception; + +public class MemberNotFoundException extends RoomescapeException { + public MemberNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/roomescape/exception/MissingTokenException.java b/src/main/java/roomescape/exception/MissingTokenException.java new file mode 100644 index 00000000..92c6fb0c --- /dev/null +++ b/src/main/java/roomescape/exception/MissingTokenException.java @@ -0,0 +1,7 @@ +package roomescape.exception; + +public class MissingTokenException extends RoomescapeException{ + public MissingTokenException(String message) { + super(message); + } +} diff --git a/src/main/java/roomescape/exception/RoomescapeException.java b/src/main/java/roomescape/exception/RoomescapeException.java new file mode 100644 index 00000000..95fbe40c --- /dev/null +++ b/src/main/java/roomescape/exception/RoomescapeException.java @@ -0,0 +1,7 @@ +package roomescape.exception; + +public class RoomescapeException extends RuntimeException { + public RoomescapeException(String message) { + super(message); + } +} From fde3d1d3c86bff76a4aea26d80d7d26da9dc8425 Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Mon, 13 Jan 2025 20:38:38 +0900 Subject: [PATCH 12/13] =?UTF-8?q?[REFACTOR]=20=EC=BB=A4=EC=8A=A4=ED=85=80?= =?UTF-8?q?=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/auth/AuthClaimsArgumentResolver.java | 3 ++- .../java/roomescape/auth/AuthCustomAnnotation.java | 13 +++++++++++++ .../reservation/ReservationController.java | 3 ++- 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 src/main/java/roomescape/auth/AuthCustomAnnotation.java diff --git a/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java b/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java index 7c93602b..9b17eb99 100644 --- a/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java +++ b/src/main/java/roomescape/auth/AuthClaimsArgumentResolver.java @@ -18,7 +18,8 @@ public class AuthClaimsArgumentResolver implements HandlerMethodArgumentResolver @Override public boolean supportsParameter(MethodParameter parameter) { - return parameter.getParameterType().equals(AuthClaims.class); + return parameter.hasParameterAnnotation(AuthCustomAnnotation.class) && + parameter.getParameterType().equals(AuthClaims.class); } @Override diff --git a/src/main/java/roomescape/auth/AuthCustomAnnotation.java b/src/main/java/roomescape/auth/AuthCustomAnnotation.java new file mode 100644 index 00000000..77ccf444 --- /dev/null +++ b/src/main/java/roomescape/auth/AuthCustomAnnotation.java @@ -0,0 +1,13 @@ +package roomescape.auth; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface AuthCustomAnnotation { +} diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index e5ccecf2..5fe95146 100644 --- a/src/main/java/roomescape/reservation/ReservationController.java +++ b/src/main/java/roomescape/reservation/ReservationController.java @@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import roomescape.auth.AuthClaims; +import roomescape.auth.AuthCustomAnnotation; import java.net.URI; import java.util.List; @@ -25,7 +26,7 @@ public List list() { } @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, AuthClaims userClaims) { + public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, @AuthCustomAnnotation AuthClaims userClaims) { if (reservationRequest.date() == null || reservationRequest.theme() == null || reservationRequest.time() == null) { From e3ae6da0c4e1229739b871f3692a5d12a222ed0d Mon Sep 17 00:00:00 2001 From: SANGHEEJEONG <147232817+SANGHEEJEONG@users.noreply.github.com> Date: Thu, 16 Jan 2025 00:52:45 +0900 Subject: [PATCH 13/13] =?UTF-8?q?[FIX]=20exceptionController=20=EC=98=A4?= =?UTF-8?q?=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/exception/ExceptionController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/roomescape/exception/ExceptionController.java b/src/main/java/roomescape/exception/ExceptionController.java index 2d924653..04a1e778 100644 --- a/src/main/java/roomescape/exception/ExceptionController.java +++ b/src/main/java/roomescape/exception/ExceptionController.java @@ -8,12 +8,12 @@ @ControllerAdvice public class ExceptionController { @ExceptionHandler(InvalidTokenException.class) - public ResponseEntity handleInvalidTokenException(InvalidLoginException e) { + public ResponseEntity handleInvalidTokenException(InvalidTokenException e) { return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage()); } @ExceptionHandler(MissingTokenException.class) - public ResponseEntity handleMissingTokenException(InvalidLoginException e) { + public ResponseEntity handleMissingTokenException(MissingTokenException e) { return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getMessage()); }