From 1c301b1a6f961a3e06814cf4a226ddc65aa0483f Mon Sep 17 00:00:00 2001 From: sangjin Date: Fri, 26 Jul 2024 16:04:37 +0900 Subject: [PATCH 1/3] =?UTF-8?q?step1=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/member/MemberController.java | 18 ++++++++ .../java/roomescape/member/MemberDao.java | 13 ++++++ .../java/roomescape/member/MemberService.java | 42 +++++++++++++++++++ src/test/java/roomescape/MissionStepTest.java | 23 ++++++---- 4 files changed, 89 insertions(+), 7 deletions(-) diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 881ae5e0d..18351ba3a 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -3,6 +3,7 @@ import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; + import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -25,6 +26,23 @@ public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member); } + @PostMapping("/login") + public ResponseEntity login(@RequestBody MemberRequest memberRequest, HttpServletResponse response) { + MemberResponse member = memberService.login(memberRequest); + Cookie cookie = new Cookie("token", memberService.getToken(member)); + cookie.setHttpOnly(true); + cookie.setPath("/"); + response.addCookie(cookie); + return ResponseEntity.ok().build(); + } + + @GetMapping("/login/check") + public ResponseEntity checkLogin(HttpServletRequest request) { + Cookie[] cookies = request.getCookies(); + MemberResponse memberResponse = memberService.checkToken(cookies); + return ResponseEntity.ok(memberResponse); + } + @PostMapping("/logout") public ResponseEntity logout(HttpServletResponse response) { Cookie cookie = new Cookie("token", ""); diff --git a/src/main/java/roomescape/member/MemberDao.java b/src/main/java/roomescape/member/MemberDao.java index 81f77f4cd..35dcfbc05 100644 --- a/src/main/java/roomescape/member/MemberDao.java +++ b/src/main/java/roomescape/member/MemberDao.java @@ -52,4 +52,17 @@ public Member findByName(String name) { name ); } + + public Member findById(final Long memberId) { + return jdbcTemplate.queryForObject( + "SELECT id, name, email, role FROM member WHERE id = ?", + (rs, rowNum) -> new Member( + rs.getLong("id"), + rs.getString("name"), + rs.getString("email"), + rs.getString("role") + ), + memberId + ); + } } diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index ccaa8cba5..7a141f382 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -2,6 +2,11 @@ import org.springframework.stereotype.Service; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; + @Service public class MemberService { private MemberDao memberDao; @@ -14,4 +19,41 @@ 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 MemberResponse login(final MemberRequest memberRequest) { + Member member = memberDao.findByEmailAndPassword(memberRequest.getEmail(), memberRequest.getPassword()); + return new MemberResponse(member.getId(), member.getName(), member.getEmail()); + } + + public String getToken(final MemberResponse member) { + String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; + String accessToken = Jwts.builder() + .setSubject(member.getId().toString()) + .claim("name", member.getName()) +// .claim("role", member.getRole()) + .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) + .compact(); + return accessToken; + } + + public MemberResponse checkToken(final Cookie[] cookies) { + String token = extractTokenFromCookie(cookies); + Long memberId = Long.valueOf(Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor("Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E=".getBytes())) + .build() + .parseClaimsJws(token) + .getBody().getSubject()); + Member member = memberDao.findById(memberId); + return new MemberResponse(member.getId(), member.getName(), member.getEmail()); + } + + private String extractTokenFromCookie(Cookie[] cookies) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals("token")) { + return cookie.getValue(); + } + } + + return ""; + } } diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 6add784bd..b0471deea 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -1,9 +1,7 @@ package roomescape; -import io.restassured.RestAssured; -import io.restassured.http.ContentType; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; @@ -11,12 +9,14 @@ import java.util.HashMap; import java.util.Map; -import static org.assertj.core.api.Assertions.assertThat; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) public class MissionStepTest { - @Test void 일단계() { Map params = new HashMap<>(); @@ -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 537e513e974e0d8b1fee71535b6fb9c968f2f682 Mon Sep 17 00:00:00 2001 From: sangjin Date: Sun, 28 Jul 2024 02:11:24 +0900 Subject: [PATCH 2/3] =?UTF-8?q?step2=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EB=A6=AC=ED=8E=99=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/roomescape/global/LoginMember.java | 31 +++++++++++ .../global/LoginMemberArgumentResolver.java | 34 ++++++++++++ .../java/roomescape/global/TokenProvider.java | 24 ++++++++ .../java/roomescape/global/WebConfig.java | 23 ++++++++ .../roomescape/member/MemberController.java | 9 ++- .../java/roomescape/member/MemberService.java | 36 ++++++------ .../reservation/ReservationController.java | 13 ++--- .../reservation/ReservationRequest.java | 10 ++++ .../reservation/ReservationResponse.java | 3 + .../reservation/ReservationService.java | 15 ++++- src/test/java/roomescape/MissionStepTest.java | 55 +++++++++++++++++-- 11 files changed, 215 insertions(+), 38 deletions(-) create mode 100644 src/main/java/roomescape/global/LoginMember.java create mode 100644 src/main/java/roomescape/global/LoginMemberArgumentResolver.java create mode 100644 src/main/java/roomescape/global/TokenProvider.java create mode 100644 src/main/java/roomescape/global/WebConfig.java diff --git a/src/main/java/roomescape/global/LoginMember.java b/src/main/java/roomescape/global/LoginMember.java new file mode 100644 index 000000000..4cab93a0a --- /dev/null +++ b/src/main/java/roomescape/global/LoginMember.java @@ -0,0 +1,31 @@ +package roomescape.global; + +public class LoginMember { + private Long id; + private String name; + private String email; + private String role; + + public LoginMember(final Long id, final String name, final String email, final String role) { + this.id = id; + this.name = name; + this.email = email; + this.role = role; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getEmail() { + return email; + } + + public String getRole() { + return role; + } +} diff --git a/src/main/java/roomescape/global/LoginMemberArgumentResolver.java b/src/main/java/roomescape/global/LoginMemberArgumentResolver.java new file mode 100644 index 000000000..fa152929b --- /dev/null +++ b/src/main/java/roomescape/global/LoginMemberArgumentResolver.java @@ -0,0 +1,34 @@ +package roomescape.global; + +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.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import jakarta.servlet.http.HttpServletRequest; +import roomescape.member.MemberResponse; +import roomescape.member.MemberService; + +@Component +public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { + private MemberService memberService; + + public LoginMemberArgumentResolver(MemberService memberService) { + this.memberService = memberService; + } + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.getParameterType().equals(LoginMember.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); + String token = memberService.extractTokenFromCookie(request.getCookies()); + MemberResponse member = memberService.findByToken(token); + return new LoginMember(member.getId(), member.getName(), member.getEmail(), "USER"); + } +} diff --git a/src/main/java/roomescape/global/TokenProvider.java b/src/main/java/roomescape/global/TokenProvider.java new file mode 100644 index 000000000..3bd9f40e3 --- /dev/null +++ b/src/main/java/roomescape/global/TokenProvider.java @@ -0,0 +1,24 @@ +package roomescape.global; + +import org.springframework.stereotype.Component; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import roomescape.member.Member; +import roomescape.member.MemberResponse; + +@Component +public class TokenProvider { + + public static final String SECRET_KEY = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; + + public String createToken(Member member) { + String accessToken = Jwts.builder() + .setSubject(member.getId().toString()) + .claim("name", member.getName()) + .claim("role", member.getRole()) + .signWith(Keys.hmacShaKeyFor(SECRET_KEY.getBytes())) + .compact(); + return accessToken; + } +} \ No newline at end of file diff --git a/src/main/java/roomescape/global/WebConfig.java b/src/main/java/roomescape/global/WebConfig.java new file mode 100644 index 000000000..383c32c1c --- /dev/null +++ b/src/main/java/roomescape/global/WebConfig.java @@ -0,0 +1,23 @@ +package roomescape.global; + +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/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 18351ba3a..c785d3a6b 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -29,17 +29,16 @@ public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { @PostMapping("/login") public ResponseEntity login(@RequestBody MemberRequest memberRequest, HttpServletResponse response) { MemberResponse member = memberService.login(memberRequest); - Cookie cookie = new Cookie("token", memberService.getToken(member)); - cookie.setHttpOnly(true); - cookie.setPath("/"); - response.addCookie(cookie); + String token = memberService.createToken(member); + memberService.createCookie(response, token); return ResponseEntity.ok().build(); } @GetMapping("/login/check") public ResponseEntity checkLogin(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); - MemberResponse memberResponse = memberService.checkToken(cookies); + String token = memberService.extractTokenFromCookie(cookies); + MemberResponse memberResponse = memberService.findByToken(token); return ResponseEntity.ok(memberResponse); } diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index 7a141f382..6a255fc28 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -2,17 +2,20 @@ import org.springframework.stereotype.Service; +import roomescape.global.TokenProvider; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; @Service public class MemberService { private MemberDao memberDao; + private TokenProvider tokenProvider; - public MemberService(MemberDao memberDao) { + public MemberService(MemberDao memberDao, TokenProvider tokenProvider) { this.memberDao = memberDao; + this.tokenProvider = tokenProvider; } public MemberResponse createMember(MemberRequest memberRequest) { @@ -25,19 +28,7 @@ public MemberResponse login(final MemberRequest memberRequest) { return new MemberResponse(member.getId(), member.getName(), member.getEmail()); } - public String getToken(final MemberResponse member) { - String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; - String accessToken = Jwts.builder() - .setSubject(member.getId().toString()) - .claim("name", member.getName()) -// .claim("role", member.getRole()) - .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) - .compact(); - return accessToken; - } - - public MemberResponse checkToken(final Cookie[] cookies) { - String token = extractTokenFromCookie(cookies); + public MemberResponse findByToken(String token) { Long memberId = Long.valueOf(Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor("Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E=".getBytes())) .build() @@ -47,13 +38,24 @@ public MemberResponse checkToken(final Cookie[] cookies) { return new MemberResponse(member.getId(), member.getName(), member.getEmail()); } - private String extractTokenFromCookie(Cookie[] cookies) { + public String extractTokenFromCookie(Cookie[] cookies) { for (Cookie cookie : cookies) { if (cookie.getName().equals("token")) { return cookie.getValue(); } } - return ""; } + + public String createToken(MemberResponse memberResponse){ + String accessToken = tokenProvider.createToken(new Member(memberResponse.getId(), memberResponse.getName(), memberResponse.getEmail(), "USER")); + return accessToken; + } + + public void createCookie(final HttpServletResponse response, final String token) { + Cookie cookie = new Cookie("token", token); + cookie.setHttpOnly(true); + cookie.setPath("/"); + response.addCookie(cookie); + } } diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index b3bef3990..ebafc9ec7 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 org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -11,6 +12,8 @@ import java.net.URI; import java.util.List; +import roomescape.global.LoginMember; + @RestController public class ReservationController { @@ -26,15 +29,9 @@ public List list() { } @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest reservationRequest) { - if (reservationRequest.getName() == null - || reservationRequest.getDate() == null - || reservationRequest.getTheme() == null - || reservationRequest.getTime() == null) { - return ResponseEntity.badRequest().build(); - } - ReservationResponse reservation = reservationService.save(reservationRequest); + public ResponseEntity createReservation(@RequestBody ReservationRequest reservationRequest, LoginMember member) { + ReservationResponse reservation = reservationService.save(member, reservationRequest); return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation); } diff --git a/src/main/java/roomescape/reservation/ReservationRequest.java b/src/main/java/roomescape/reservation/ReservationRequest.java index 19f441246..c99f1ef46 100644 --- a/src/main/java/roomescape/reservation/ReservationRequest.java +++ b/src/main/java/roomescape/reservation/ReservationRequest.java @@ -6,6 +6,16 @@ public class ReservationRequest { private Long theme; private Long time; + public ReservationRequest() { + } + + public ReservationRequest(final String name, final String date, final Long theme, final Long time) { + this.name = name; + this.date = date; + this.theme = theme; + this.time = time; + } + public String getName() { return name; } diff --git a/src/main/java/roomescape/reservation/ReservationResponse.java b/src/main/java/roomescape/reservation/ReservationResponse.java index 41360a363..6a118726e 100644 --- a/src/main/java/roomescape/reservation/ReservationResponse.java +++ b/src/main/java/roomescape/reservation/ReservationResponse.java @@ -7,6 +7,9 @@ public class ReservationResponse { private String date; private String time; + public ReservationResponse() { + } + public ReservationResponse(Long id, String name, String theme, String date, String time) { this.id = id; this.name = name; diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java index bd3313328..f60a3b20a 100644 --- a/src/main/java/roomescape/reservation/ReservationService.java +++ b/src/main/java/roomescape/reservation/ReservationService.java @@ -4,6 +4,8 @@ import java.util.List; +import roomescape.global.LoginMember; + @Service public class ReservationService { private ReservationDao reservationDao; @@ -12,10 +14,19 @@ public ReservationService(ReservationDao reservationDao) { this.reservationDao = reservationDao; } - public ReservationResponse save(ReservationRequest reservationRequest) { + public ReservationResponse save(LoginMember member, ReservationRequest reservationRequest) { + if (reservationRequest.getName() == null) { + Reservation reservation = reservationDao.save(setReservationMemberName(member, reservationRequest)); + return new ReservationResponse(reservation.getId(), reservation.getName(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); + } + + Reservation reservation = reservationDao.save(reservationRequest); + return new ReservationResponse(reservation.getId(), reservation.getName(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); + } - return new ReservationResponse(reservation.getId(), reservationRequest.getName(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); + private ReservationRequest setReservationMemberName(LoginMember member, ReservationRequest reservationRequest) { + return new ReservationRequest(member.getName(), reservationRequest.getDate(), reservationRequest.getTheme(), reservationRequest.getTime()); } public void deleteById(Long id) { diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index b0471deea..7a158dd80 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -13,15 +13,32 @@ import io.restassured.http.ContentType; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; +import roomescape.reservation.ReservationResponse; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) public class MissionStepTest { + @Test void 일단계() { + + String token = createToken("admin@email.com", "password"); + + 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("어드민"); + } + + private String createToken(final String email, final String password) { Map params = new HashMap<>(); - params.put("email", "admin@email.com"); - params.put("password", "password"); + params.put("email", email); + params.put("password", password); ExtractableResponse response = RestAssured.given().log().all() .contentType(ContentType.JSON) @@ -33,15 +50,41 @@ public class MissionStepTest { String token = response.headers().get("Set-Cookie").getValue().split(";")[0].split("=")[1]; assertThat(token).isNotBlank(); + return token; + } - ExtractableResponse checkResponse = RestAssured.given().log().all() + @Test + void 이단계() { + + String token = createToken("admin@email.com", "password"); // 일단계에서 토큰을 추출하는 로직을 메서드로 따로 만들어서 활용하세요. + + Map params = new HashMap<>(); + 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) .cookie("token", token) - .when().get("/login/check") + .contentType(ContentType.JSON) + .post("/reservations") .then().log().all() - .statusCode(200) .extract(); - assertThat(checkResponse.body().jsonPath().getString("name")).isEqualTo("어드민"); + assertThat(adminResponse.statusCode()).isEqualTo(201); + assertThat(adminResponse.as(ReservationResponse.class).getName()).isEqualTo("브라운"); } } \ No newline at end of file From 972780c1155d6a4d883079848f9fe15d13fb64db Mon Sep 17 00:00:00 2001 From: sangjin Date: Sun, 28 Jul 2024 02:39:25 +0900 Subject: [PATCH 3/3] =?UTF-8?q?step3=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/roomescape/global/RoleHandler.java | 34 +++++++++++++++++++ .../java/roomescape/global/WebConfig.java | 11 +++++- .../roomescape/member/MemberResponse.java | 8 ++++- .../java/roomescape/member/MemberService.java | 6 ++-- src/test/java/roomescape/MissionStepTest.java | 19 +++++++++++ 5 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 src/main/java/roomescape/global/RoleHandler.java diff --git a/src/main/java/roomescape/global/RoleHandler.java b/src/main/java/roomescape/global/RoleHandler.java new file mode 100644 index 000000000..727813703 --- /dev/null +++ b/src/main/java/roomescape/global/RoleHandler.java @@ -0,0 +1,34 @@ +package roomescape.global; + +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import roomescape.member.MemberResponse; +import roomescape.member.MemberService; + +@Component +public class RoleHandler implements HandlerInterceptor { + + private final MemberService memberService; + + public RoleHandler(final MemberService memberService) { + this.memberService = memberService; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + Cookie[] cookie =request.getCookies(); + String token = memberService.extractTokenFromCookie(cookie); + MemberResponse member = memberService.findByToken(token); + + if (member == null || !member.getRole().equals("ADMIN")) { + response.setStatus(401); + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/roomescape/global/WebConfig.java b/src/main/java/roomescape/global/WebConfig.java index 383c32c1c..be9499153 100644 --- a/src/main/java/roomescape/global/WebConfig.java +++ b/src/main/java/roomescape/global/WebConfig.java @@ -2,6 +2,7 @@ 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; @@ -11,13 +12,21 @@ public class WebConfig implements WebMvcConfigurer { private final LoginMemberArgumentResolver loginMemberArgumentResolver; + private final RoleHandler roleHandler; - public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver) { + public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver, final RoleHandler roleHandler) { this.loginMemberArgumentResolver = loginMemberArgumentResolver; + this.roleHandler = roleHandler; } @Override public void addArgumentResolvers(List resolvers) { resolvers.add(loginMemberArgumentResolver); } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(roleHandler) + .addPathPatterns("/admin/**"); + } } diff --git a/src/main/java/roomescape/member/MemberResponse.java b/src/main/java/roomescape/member/MemberResponse.java index b9fa3b97a..2a958c20d 100644 --- a/src/main/java/roomescape/member/MemberResponse.java +++ b/src/main/java/roomescape/member/MemberResponse.java @@ -4,11 +4,13 @@ public class MemberResponse { private Long id; private String name; private String email; + private String role; - public MemberResponse(Long id, String name, String email) { + public MemberResponse(Long id, String name, String email, String role) { this.id = id; this.name = name; this.email = email; + this.role = role; } public Long getId() { @@ -22,4 +24,8 @@ public String getName() { public String getEmail() { return email; } + + public String getRole() { + return role; + } } diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index 6a255fc28..8d959a269 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -20,12 +20,12 @@ public MemberService(MemberDao memberDao, TokenProvider tokenProvider) { 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()); + return new MemberResponse(member.getId(), member.getName(), member.getEmail(), member.getRole()); } public MemberResponse login(final MemberRequest memberRequest) { Member member = memberDao.findByEmailAndPassword(memberRequest.getEmail(), memberRequest.getPassword()); - return new MemberResponse(member.getId(), member.getName(), member.getEmail()); + return new MemberResponse(member.getId(), member.getName(), member.getEmail() , member.getRole()); } public MemberResponse findByToken(String token) { @@ -35,7 +35,7 @@ public MemberResponse findByToken(String token) { .parseClaimsJws(token) .getBody().getSubject()); Member member = memberDao.findById(memberId); - return new MemberResponse(member.getId(), member.getName(), member.getEmail()); + return new MemberResponse(member.getId(), member.getName(), member.getEmail(), member.getRole()); } public String extractTokenFromCookie(Cookie[] cookies) { diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 7a158dd80..f92befba9 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -87,4 +87,23 @@ private String createToken(final String email, final String password) { assertThat(adminResponse.statusCode()).isEqualTo(201); assertThat(adminResponse.as(ReservationResponse.class).getName()).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); + } } \ No newline at end of file