From 5fac77bf21bf9ecd7de86358f2d42ca6f5071f4e Mon Sep 17 00:00:00 2001 From: "jieun4510@sju.ac.kr" Date: Mon, 24 Jun 2024 17:34:36 +0900 Subject: [PATCH 1/7] feat: step 1 --- src/main/README.md | 10 ++++ .../java/roomescape/auth/AuthController.java | 47 +++++++++++++++++++ .../java/roomescape/auth/AuthService.java | 41 ++++++++++++++++ .../auth/AuthorizationExtractor.java | 27 +++++++++++ .../java/roomescape/auth/TokenProvider.java | 23 +++++++++ .../java/roomescape/auth/TokenRequest.java | 23 +++++++++ .../java/roomescape/auth/TokenResponse.java | 17 +++++++ .../roomescape/member/MemberController.java | 2 - .../java/roomescape/member/MemberDao.java | 13 +++++ .../java/roomescape/member/MemberService.java | 2 + src/test/java/roomescape/MissionStepTest.java | 11 ++++- 11 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 src/main/README.md create mode 100644 src/main/java/roomescape/auth/AuthController.java create mode 100644 src/main/java/roomescape/auth/AuthService.java create mode 100644 src/main/java/roomescape/auth/AuthorizationExtractor.java create mode 100644 src/main/java/roomescape/auth/TokenProvider.java create mode 100644 src/main/java/roomescape/auth/TokenRequest.java create mode 100644 src/main/java/roomescape/auth/TokenResponse.java diff --git a/src/main/README.md b/src/main/README.md new file mode 100644 index 000000000..e0a7e0f4f --- /dev/null +++ b/src/main/README.md @@ -0,0 +1,10 @@ +## 1단계 요구사항 +### - 로그인 기능 구현 +- [x] email, password로 멤버 조회 +- [x] 조회한 멤버로 토큰 생성 +- [x] 쿠키 생성 + + +### - 인증 정보 조회 기능 구현 +- [x] 쿠키에서 토큰 정보 추출 +- [x] 멤버를 찾아 멤버 정보 응답 diff --git a/src/main/java/roomescape/auth/AuthController.java b/src/main/java/roomescape/auth/AuthController.java new file mode 100644 index 000000000..bf144a0e5 --- /dev/null +++ b/src/main/java/roomescape/auth/AuthController.java @@ -0,0 +1,47 @@ +package roomescape.auth; + +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.*; +import roomescape.member.MemberResponse; + +@RestController +public class AuthController { + private final AuthService authService; + + public AuthController(AuthService authService) { + this.authService = authService; + } + + @PostMapping("/login") + public ResponseEntity login(@RequestBody TokenRequest tokenRequest, HttpServletResponse response) { + TokenResponse tokenResponse = authService.createToken(tokenRequest); + + // cookie 생성 + Cookie cookie = new Cookie("token", tokenResponse.getAccessToken()); + cookie.setHttpOnly(true); + cookie.setPath("/"); + response.addCookie(cookie); + + return ResponseEntity.ok().body(tokenResponse); + } + + @GetMapping("/login/check") + public ResponseEntity checkLogin(HttpServletRequest request) { + Cookie[] cookies = request.getCookies(); + String token = extractTokenFromCookie(cookies); + MemberResponse memberResponse = authService.findMemberByToken(token); + return ResponseEntity.ok().body(memberResponse); + } + + private String extractTokenFromCookie(Cookie[] cookies) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals("token")){ + return cookie.getValue(); + } + } + return ""; + } +} diff --git a/src/main/java/roomescape/auth/AuthService.java b/src/main/java/roomescape/auth/AuthService.java new file mode 100644 index 000000000..3c74675ce --- /dev/null +++ b/src/main/java/roomescape/auth/AuthService.java @@ -0,0 +1,41 @@ +package roomescape.auth; + +import org.springframework.stereotype.Service; +import roomescape.member.Member; +import roomescape.member.MemberDao; +import roomescape.member.MemberResponse; + +@Service +public class AuthService { + private MemberDao memberDao; + private TokenProvider tokenProvider; + private AuthorizationExtractor authorizationExtractor; + + public AuthService(MemberDao memberDao, TokenProvider tokenProvider, AuthorizationExtractor authorizationExtractor) { + this.memberDao = memberDao; + this.tokenProvider = tokenProvider; + this.authorizationExtractor = authorizationExtractor; + } + + public Member findMember(String email, String password) { + Member member = memberDao.findByEmailAndPassword(email, password); + if (member == null) { + throw new IllegalArgumentException(); + } + return memberDao.findByEmailAndPassword(email, password); + } + + public TokenResponse createToken(TokenRequest tokenRequest) { + Member member = findMember(tokenRequest.getEmail(), tokenRequest.getPassword()); + + String accessToken = tokenProvider.createToken(member); + + return new TokenResponse(accessToken); + } + + public MemberResponse findMemberByToken(String token) { + Long id = authorizationExtractor.extractMemberId(token); + Member member = memberDao.findById(id); + return new MemberResponse(member.getId(), member.getName(), member.getEmail()); + } +} diff --git a/src/main/java/roomescape/auth/AuthorizationExtractor.java b/src/main/java/roomescape/auth/AuthorizationExtractor.java new file mode 100644 index 000000000..2466b7e52 --- /dev/null +++ b/src/main/java/roomescape/auth/AuthorizationExtractor.java @@ -0,0 +1,27 @@ +package roomescape.auth; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import org.springframework.stereotype.Component; +import roomescape.member.Member; +import roomescape.member.MemberDao; + +@Component +public class AuthorizationExtractor { + private String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; + private MemberDao memberDao; + + public AuthorizationExtractor(MemberDao memberDao) { + this.memberDao = memberDao; + } + + public Long extractMemberId (String token) { + Long memberId = Long.valueOf(Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) + .build() + .parseClaimsJws(token) + .getBody().getSubject()); + return memberId; + } +} + diff --git a/src/main/java/roomescape/auth/TokenProvider.java b/src/main/java/roomescape/auth/TokenProvider.java new file mode 100644 index 000000000..4df58d721 --- /dev/null +++ b/src/main/java/roomescape/auth/TokenProvider.java @@ -0,0 +1,23 @@ +package roomescape.auth; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import org.springframework.stereotype.Component; +import roomescape.member.Member; + +import java.nio.charset.StandardCharsets; + +@Component +public class TokenProvider { + private String secretKey = "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(secretKey.getBytes())) + .compact(); + return accessToken; + } +} diff --git a/src/main/java/roomescape/auth/TokenRequest.java b/src/main/java/roomescape/auth/TokenRequest.java new file mode 100644 index 000000000..110928896 --- /dev/null +++ b/src/main/java/roomescape/auth/TokenRequest.java @@ -0,0 +1,23 @@ +package roomescape.auth; + +public class TokenRequest { + private String email; + private String password; + + public TokenRequest() { + + } + + public TokenRequest(String email, String password) { + this.email = email; + this.password = password; + } + + public String getEmail() { + return email; + } + + public String getPassword() { + return password; + } +} diff --git a/src/main/java/roomescape/auth/TokenResponse.java b/src/main/java/roomescape/auth/TokenResponse.java new file mode 100644 index 000000000..2acb82046 --- /dev/null +++ b/src/main/java/roomescape/auth/TokenResponse.java @@ -0,0 +1,17 @@ +package roomescape.auth; + +public class TokenResponse { + private String accessToken; + + public TokenResponse() { + + } + + public TokenResponse(String accessToken) { + this.accessToken = accessToken; + } + + public String getAccessToken() { + return accessToken; + } +} diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 881ae5e0d..fe009d062 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -1,10 +1,8 @@ package roomescape.member; 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; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; diff --git a/src/main/java/roomescape/member/MemberDao.java b/src/main/java/roomescape/member/MemberDao.java index 81f77f4cd..114fd342d 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(Long id) { + 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") + ), + id + ); + } } diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index ccaa8cba5..36d2cfbba 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -14,4 +14,6 @@ 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()); } + + } diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index 6add784bd..765c287d7 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 e862024feb3e9d600d2bea4d1e6e4ee0e5864f70 Mon Sep 17 00:00:00 2001 From: "jieun4510@sju.ac.kr" Date: Tue, 2 Jul 2024 12:45:25 +0900 Subject: [PATCH 2/7] feat: step2 --- build.gradle | 1 + src/main/java/roomescape/LoginMember.java | 40 ++++++++++++++ .../LoginMemberArgumentResolver.java | 36 +++++++++++++ src/main/java/roomescape/WebConfig.java | 23 ++++++++ .../auth/AuthorizationExtractor.java | 2 + .../java/roomescape/member/MemberService.java | 16 +++++- .../reservation/ReservationController.java | 11 ++-- .../reservation/ReservationDao.java | 6 +-- .../reservation/ReservationService.java | 15 ++++-- src/test/java/roomescape/MissionStepTest.java | 53 +++++++++++++++++++ 10 files changed, 191 insertions(+), 12 deletions(-) create mode 100644 src/main/java/roomescape/LoginMember.java create mode 100644 src/main/java/roomescape/LoginMemberArgumentResolver.java create mode 100644 src/main/java/roomescape/WebConfig.java diff --git a/build.gradle b/build.gradle index 8d52aebc6..b820ecb40 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,7 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.11.2' implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' implementation 'io.jsonwebtoken:jjwt-gson:0.11.2' + implementation 'org.projectlombok:lombok:1.18.22' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' diff --git a/src/main/java/roomescape/LoginMember.java b/src/main/java/roomescape/LoginMember.java new file mode 100644 index 000000000..bf1f28304 --- /dev/null +++ b/src/main/java/roomescape/LoginMember.java @@ -0,0 +1,40 @@ +package roomescape; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public class LoginMember { + private Long id; + private String name; + private String email; + private String role; + + public LoginMember(Long id, String name, String email, 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/LoginMemberArgumentResolver.java b/src/main/java/roomescape/LoginMemberArgumentResolver.java new file mode 100644 index 000000000..6030a2812 --- /dev/null +++ b/src/main/java/roomescape/LoginMemberArgumentResolver.java @@ -0,0 +1,36 @@ +package roomescape; + +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.core.MethodParameter; +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 roomescape.auth.AuthorizationExtractor; +import roomescape.member.Member; +import roomescape.member.MemberDao; +import roomescape.member.MemberService; + +import java.util.Arrays; + +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 httpServletRequest = (HttpServletRequest) webRequest.getNativeRequest(); + + Member member = memberService.getMemberFromCookie(httpServletRequest); + + return new LoginMember(member.getId(), member.getName(), member.getEmail(), member.getRole()); + } +} diff --git a/src/main/java/roomescape/WebConfig.java b/src/main/java/roomescape/WebConfig.java new file mode 100644 index 000000000..f63a59a96 --- /dev/null +++ b/src/main/java/roomescape/WebConfig.java @@ -0,0 +1,23 @@ +package roomescape; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import roomescape.auth.AuthorizationExtractor; +import roomescape.member.MemberDao; +import roomescape.member.MemberService; + +import java.util.List; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Autowired + private MemberService memberService; + + @Override + public void addArgumentResolvers(List argumentResolvers) { + argumentResolvers.add(new LoginMemberArgumentResolver(memberService)); + } +} diff --git a/src/main/java/roomescape/auth/AuthorizationExtractor.java b/src/main/java/roomescape/auth/AuthorizationExtractor.java index 2466b7e52..b967ed92c 100644 --- a/src/main/java/roomescape/auth/AuthorizationExtractor.java +++ b/src/main/java/roomescape/auth/AuthorizationExtractor.java @@ -23,5 +23,7 @@ public Long extractMemberId (String token) { .getBody().getSubject()); return memberId; } + + } diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index 36d2cfbba..520f79967 100644 --- a/src/main/java/roomescape/member/MemberService.java +++ b/src/main/java/roomescape/member/MemberService.java @@ -1,13 +1,20 @@ package roomescape.member; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.stereotype.Service; +import roomescape.auth.AuthorizationExtractor; + +import java.util.Arrays; @Service public class MemberService { private MemberDao memberDao; + private AuthorizationExtractor authorizationExtractor; - public MemberService(MemberDao memberDao) { + public MemberService(MemberDao memberDao, AuthorizationExtractor authorizationExtractor) { this.memberDao = memberDao; + this.authorizationExtractor = authorizationExtractor; } public MemberResponse createMember(MemberRequest memberRequest) { @@ -15,5 +22,10 @@ public MemberResponse createMember(MemberRequest memberRequest) { return new MemberResponse(member.getId(), member.getName(), member.getEmail()); } - + public Member getMemberFromCookie(HttpServletRequest httpServletRequest) { + Cookie[] cookies = httpServletRequest.getCookies(); + String token = Arrays.stream(cookies).filter(cookie -> cookie.getName().equals("token")).findFirst().map(Cookie::getValue).orElseThrow(); + Member member = memberDao.findById(authorizationExtractor.extractMemberId(token)); + return member; + } } diff --git a/src/main/java/roomescape/reservation/ReservationController.java b/src/main/java/roomescape/reservation/ReservationController.java index b3bef3990..e03c59ed4 100644 --- a/src/main/java/roomescape/reservation/ReservationController.java +++ b/src/main/java/roomescape/reservation/ReservationController.java @@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import roomescape.LoginMember; import java.net.URI; import java.util.List; @@ -26,14 +27,16 @@ public List list() { } @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest reservationRequest) { - if (reservationRequest.getName() == null - || reservationRequest.getDate() == null + public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, LoginMember member) { + System.out.println(member.getName()); + System.out.println(member.getId()); + System.out.println(); + if (reservationRequest.getDate() == null || reservationRequest.getTheme() == null || reservationRequest.getTime() == null) { return ResponseEntity.badRequest().build(); } - ReservationResponse reservation = reservationService.save(reservationRequest); + ReservationResponse reservation = reservationService.save(reservationRequest, member); return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation); } diff --git a/src/main/java/roomescape/reservation/ReservationDao.java b/src/main/java/roomescape/reservation/ReservationDao.java index a4972430c..b029b348c 100644 --- a/src/main/java/roomescape/reservation/ReservationDao.java +++ b/src/main/java/roomescape/reservation/ReservationDao.java @@ -43,12 +43,12 @@ public List findAll() { ))); } - public Reservation save(ReservationRequest reservationRequest) { + public Reservation save(ReservationRequest reservationRequest, String name) { 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.setString(2, name); ps.setLong(3, reservationRequest.getTheme()); ps.setLong(4, reservationRequest.getTime()); return ps; @@ -64,7 +64,7 @@ public Reservation save(ReservationRequest reservationRequest) { return new Reservation( keyHolder.getKey().longValue(), - reservationRequest.getName(), + name, reservationRequest.getDate(), time, theme diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java index bd3313328..fb67f984f 100644 --- a/src/main/java/roomescape/reservation/ReservationService.java +++ b/src/main/java/roomescape/reservation/ReservationService.java @@ -1,6 +1,7 @@ package roomescape.reservation; import org.springframework.stereotype.Service; +import roomescape.LoginMember; import java.util.List; @@ -12,10 +13,18 @@ public ReservationService(ReservationDao reservationDao) { this.reservationDao = reservationDao; } - public ReservationResponse save(ReservationRequest reservationRequest) { - Reservation reservation = reservationDao.save(reservationRequest); + public ReservationResponse save(ReservationRequest reservationRequest, LoginMember member) { - return new ReservationResponse(reservation.getId(), reservationRequest.getName(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); + String name; + if (reservationRequest.getName() == null) { + name = member.getName(); + } + else { + name = reservationRequest.getName(); + } + Reservation reservation = reservationDao.save(reservationRequest, name); + + return new ReservationResponse(reservation.getId(), reservation.getName(), 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 765c287d7..af6337f91 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; @@ -44,4 +45,56 @@ public class MissionStepTest { assertThat(checkResponse.body().jsonPath().getString("name")).isEqualTo("어드민"); } + + @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) + .contentType(ContentType.JSON) + .post("/reservations") + .then().log().all() + .extract(); + + assertThat(adminResponse.statusCode()).isEqualTo(201); + assertThat(adminResponse.as(ReservationResponse.class).getName()).isEqualTo("브라운"); + } + + private String createToken(String s, String password) { + Map params = new HashMap<>(); + params.put("email", s); + params.put("password", password); + + ExtractableResponse response = RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(params) + .when().post("/login") + .then().log().all() + .statusCode(200) + .extract(); + + String token = response.headers().get("Set-Cookie").getValue().split(";")[0].split("=")[1]; + assertThat(token).isNotBlank(); + return token; + } } \ No newline at end of file From 4334425befc679335d7859704e7efeb07cb13265 Mon Sep 17 00:00:00 2001 From: "jieun4510@sju.ac.kr" Date: Tue, 2 Jul 2024 13:17:05 +0900 Subject: [PATCH 3/7] feat: step3 --- .../java/roomescape/AdminInterceptor.java | 25 +++++++++++++++++++ src/main/java/roomescape/WebConfig.java | 6 +++++ src/test/java/roomescape/MissionStepTest.java | 22 ++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 src/main/java/roomescape/AdminInterceptor.java diff --git a/src/main/java/roomescape/AdminInterceptor.java b/src/main/java/roomescape/AdminInterceptor.java new file mode 100644 index 000000000..320388506 --- /dev/null +++ b/src/main/java/roomescape/AdminInterceptor.java @@ -0,0 +1,25 @@ +package roomescape; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.servlet.HandlerInterceptor; +import roomescape.member.Member; +import roomescape.member.MemberService; + +public class AdminInterceptor implements HandlerInterceptor { + private MemberService memberService; + + public AdminInterceptor(MemberService memberService) { + this.memberService = memberService; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + Member member = memberService.getMemberFromCookie(request); + + if (member == null || !member.getRole().equals("ADMIN")) { + response.setStatus(401); + } + return true; + } +} diff --git a/src/main/java/roomescape/WebConfig.java b/src/main/java/roomescape/WebConfig.java index f63a59a96..532707371 100644 --- a/src/main/java/roomescape/WebConfig.java +++ b/src/main/java/roomescape/WebConfig.java @@ -4,6 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired; 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 roomescape.auth.AuthorizationExtractor; import roomescape.member.MemberDao; @@ -20,4 +21,9 @@ public class WebConfig implements WebMvcConfigurer { public void addArgumentResolvers(List argumentResolvers) { argumentResolvers.add(new LoginMemberArgumentResolver(memberService)); } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new AdminInterceptor(memberService)).addPathPatterns("/admin/**"); + } } diff --git a/src/test/java/roomescape/MissionStepTest.java b/src/test/java/roomescape/MissionStepTest.java index af6337f91..87ab34436 100644 --- a/src/test/java/roomescape/MissionStepTest.java +++ b/src/test/java/roomescape/MissionStepTest.java @@ -97,4 +97,26 @@ private String createToken(String s, String password) { assertThat(token).isNotBlank(); return token; } + + + @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 From 0b3512431d7934f756f391e77be2ae74dac879f7 Mon Sep 17 00:00:00 2001 From: "jieun4510@sju.ac.kr" Date: Wed, 3 Jul 2024 15:56:31 +0900 Subject: [PATCH 4/7] feat: step 4 --- build.gradle | 3 +- src/main/README.md | 13 +- src/main/java/roomescape/member/Member.java | 8 + .../roomescape/reservation/Reservation.java | 18 +- .../reservation/ReservationDao.java | 254 +++++++++--------- .../reservation/ReservationRepository.java | 10 + .../reservation/ReservationService.java | 29 +- src/main/java/roomescape/theme/Theme.java | 8 + .../roomescape/theme/ThemeRepository.java | 7 + src/main/java/roomescape/time/Time.java | 7 + .../java/roomescape/time/TimeRepository.java | 7 + .../java/roomescape/time/TimeService.java | 26 +- src/test/java/roomescape/JpaTest.java | 31 +++ src/test/java/roomescape/MissionStepTest.java | 1 - 14 files changed, 262 insertions(+), 160 deletions(-) create mode 100644 src/main/java/roomescape/reservation/ReservationRepository.java create mode 100644 src/main/java/roomescape/theme/ThemeRepository.java create mode 100644 src/main/java/roomescape/time/TimeRepository.java create mode 100644 src/test/java/roomescape/JpaTest.java diff --git a/build.gradle b/build.gradle index b820ecb40..ece4bd31a 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,8 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - implementation 'org.springframework.boot:spring-boot-starter-jdbc' + // implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.0.0' diff --git a/src/main/README.md b/src/main/README.md index e0a7e0f4f..5efbf8c69 100644 --- a/src/main/README.md +++ b/src/main/README.md @@ -1,10 +1,5 @@ -## 1단계 요구사항 -### - 로그인 기능 구현 -- [x] email, password로 멤버 조회 -- [x] 조회한 멤버로 토큰 생성 -- [x] 쿠키 생성 +## 4단계 요구사항 +### - JPA 활용하여 데이터베이스 접근 +- [ ] gradle 의존성 추가 +- [ ] 엔티티 매핑 - -### - 인증 정보 조회 기능 구현 -- [x] 쿠키에서 토큰 정보 추출 -- [x] 멤버를 찾아 멤버 정보 응답 diff --git a/src/main/java/roomescape/member/Member.java b/src/main/java/roomescape/member/Member.java index 903aaa9b0..60b2e8602 100644 --- a/src/main/java/roomescape/member/Member.java +++ b/src/main/java/roomescape/member/Member.java @@ -1,6 +1,14 @@ package roomescape.member; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity public class Member { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String email; diff --git a/src/main/java/roomescape/reservation/Reservation.java b/src/main/java/roomescape/reservation/Reservation.java index 83a7edf1b..e74f934a4 100644 --- a/src/main/java/roomescape/reservation/Reservation.java +++ b/src/main/java/roomescape/reservation/Reservation.java @@ -1,21 +1,37 @@ package roomescape.reservation; +import jakarta.persistence.*; +import roomescape.member.Member; import roomescape.theme.Theme; import roomescape.time.Time; +@Entity public class Reservation { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String date; + + @ManyToOne + @JoinColumn(name = "time_id") private Time time; + + @ManyToOne + @JoinColumn(name = "theme_id") private Theme theme; - public Reservation(Long id, String name, String date, Time time, Theme theme) { + @ManyToOne + @JoinColumn(name = "member_id") + private Member member; + + public Reservation(Long id, String name, String date, Time time, Theme theme, Member member) { this.id = id; this.name = name; this.date = date; this.time = time; this.theme = theme; + this.member = member; } public Reservation(String name, String date, Time time, Theme theme) { diff --git a/src/main/java/roomescape/reservation/ReservationDao.java b/src/main/java/roomescape/reservation/ReservationDao.java index b029b348c..447d7bbbe 100644 --- a/src/main/java/roomescape/reservation/ReservationDao.java +++ b/src/main/java/roomescape/reservation/ReservationDao.java @@ -1,127 +1,127 @@ -package roomescape.reservation; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; -import roomescape.theme.Theme; -import roomescape.time.Time; - -import java.sql.PreparedStatement; -import java.util.List; - -@Repository -public class ReservationDao { - - private final JdbcTemplate jdbcTemplate; - - public ReservationDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public List findAll() { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id", - - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } - - public Reservation save(ReservationRequest reservationRequest, String name) { - 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, name); - ps.setLong(3, reservationRequest.getTheme()); - ps.setLong(4, reservationRequest.getTime()); - 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()); - - Theme theme = jdbcTemplate.queryForObject("SELECT * FROM theme WHERE id = ?", - (rs, rowNum) -> new Theme(rs.getLong("id"), rs.getString("name"), rs.getString("description")), - reservationRequest.getTheme()); - - return new Reservation( - keyHolder.getKey().longValue(), - name, - reservationRequest.getDate(), - time, - theme - ); - } - - public void deleteById(Long id) { - jdbcTemplate.update("DELETE FROM reservation WHERE id = ?", id); - } - - public List findReservationsByDateAndTheme(String date, Long themeId) { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id" + - "WHERE r.date = ? AND r.theme_id = ?", - new Object[]{date, themeId}, - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } - - public List findByDateAndThemeId(String date, Long themeId) { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id " + - "WHERE r.date = ? AND r.theme_id = ?", - new Object[]{date, themeId}, - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } -} +//package roomescape.reservation; +// +//import org.springframework.jdbc.core.JdbcTemplate; +//import org.springframework.jdbc.support.GeneratedKeyHolder; +//import org.springframework.jdbc.support.KeyHolder; +//import org.springframework.stereotype.Repository; +//import roomescape.theme.Theme; +//import roomescape.time.Time; +// +//import java.sql.PreparedStatement; +//import java.util.List; +// +//@Repository +//public class ReservationDao { +// +// private final JdbcTemplate jdbcTemplate; +// +// public ReservationDao(JdbcTemplate jdbcTemplate) { +// this.jdbcTemplate = jdbcTemplate; +// } +// +// public List findAll() { +// return jdbcTemplate.query( +// "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + +// "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + +// "ti.id AS time_id, ti.time_value AS time_value " + +// "FROM reservation r " + +// "JOIN theme t ON r.theme_id = t.id " + +// "JOIN time ti ON r.time_id = ti.id", +// +// (rs, rowNum) -> new Reservation( +// rs.getLong("reservation_id"), +// rs.getString("reservation_name"), +// rs.getString("reservation_date"), +// new Time( +// rs.getLong("time_id"), +// rs.getString("time_value") +// ), +// new Theme( +// rs.getLong("theme_id"), +// rs.getString("theme_name"), +// rs.getString("theme_description") +// ))); +// } +// +// public Reservation save(ReservationRequest reservationRequest, String name) { +// 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, name); +// ps.setLong(3, reservationRequest.getTheme()); +// ps.setLong(4, reservationRequest.getTime()); +// 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()); +// +// Theme theme = jdbcTemplate.queryForObject("SELECT * FROM theme WHERE id = ?", +// (rs, rowNum) -> new Theme(rs.getLong("id"), rs.getString("name"), rs.getString("description")), +// reservationRequest.getTheme()); +// +// return new Reservation( +// keyHolder.getKey().longValue(), +// name, +// reservationRequest.getDate(), +// time, +// theme +// ); +// } +// +// public void deleteById(Long id) { +// jdbcTemplate.update("DELETE FROM reservation WHERE id = ?", id); +// } +// +// public List findReservationsByDateAndTheme(String date, Long themeId) { +// return jdbcTemplate.query( +// "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + +// "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + +// "ti.id AS time_id, ti.time_value AS time_value " + +// "FROM reservation r " + +// "JOIN theme t ON r.theme_id = t.id " + +// "JOIN time ti ON r.time_id = ti.id" + +// "WHERE r.date = ? AND r.theme_id = ?", +// new Object[]{date, themeId}, +// (rs, rowNum) -> new Reservation( +// rs.getLong("reservation_id"), +// rs.getString("reservation_name"), +// rs.getString("reservation_date"), +// new Time( +// rs.getLong("time_id"), +// rs.getString("time_value") +// ), +// new Theme( +// rs.getLong("theme_id"), +// rs.getString("theme_name"), +// rs.getString("theme_description") +// ))); +// } +// +// public List findByDateAndThemeId(String date, Long themeId) { +// return jdbcTemplate.query( +// "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + +// "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + +// "ti.id AS time_id, ti.time_value AS time_value " + +// "FROM reservation r " + +// "JOIN theme t ON r.theme_id = t.id " + +// "JOIN time ti ON r.time_id = ti.id " + +// "WHERE r.date = ? AND r.theme_id = ?", +// new Object[]{date, themeId}, +// (rs, rowNum) -> new Reservation( +// rs.getLong("reservation_id"), +// rs.getString("reservation_name"), +// rs.getString("reservation_date"), +// new Time( +// rs.getLong("time_id"), +// rs.getString("time_value") +// ), +// new Theme( +// rs.getLong("theme_id"), +// rs.getString("theme_name"), +// rs.getString("theme_description") +// ))); +// } +//} diff --git a/src/main/java/roomescape/reservation/ReservationRepository.java b/src/main/java/roomescape/reservation/ReservationRepository.java new file mode 100644 index 000000000..2382bb2da --- /dev/null +++ b/src/main/java/roomescape/reservation/ReservationRepository.java @@ -0,0 +1,10 @@ +package roomescape.reservation; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface ReservationRepository extends JpaRepository { + + List findByDateAndThemeId(String date, Long themeId); +} diff --git a/src/main/java/roomescape/reservation/ReservationService.java b/src/main/java/roomescape/reservation/ReservationService.java index fb67f984f..7a849f323 100644 --- a/src/main/java/roomescape/reservation/ReservationService.java +++ b/src/main/java/roomescape/reservation/ReservationService.java @@ -2,15 +2,23 @@ import org.springframework.stereotype.Service; import roomescape.LoginMember; +import roomescape.theme.Theme; +import roomescape.theme.ThemeRepository; +import roomescape.time.Time; +import roomescape.time.TimeRepository; import java.util.List; @Service public class ReservationService { - private ReservationDao reservationDao; - - public ReservationService(ReservationDao reservationDao) { - this.reservationDao = reservationDao; + private ReservationRepository reservationRepository; + private TimeRepository timeRepository; + private ThemeRepository themeRepository; + + public ReservationService(ReservationRepository reservationRepository, TimeRepository timeRepository, ThemeRepository themeRepository) { + this.reservationRepository = reservationRepository; + this.timeRepository = timeRepository; + this.themeRepository = themeRepository; } public ReservationResponse save(ReservationRequest reservationRequest, LoginMember member) { @@ -22,18 +30,21 @@ public ReservationResponse save(ReservationRequest reservationRequest, LoginMemb else { name = reservationRequest.getName(); } - Reservation reservation = reservationDao.save(reservationRequest, name); + + Theme theme = themeRepository.getReferenceById(reservationRequest.getTheme()); + Time time = timeRepository.getReferenceById(reservationRequest.getTime()); + + Reservation reservation = new Reservation(name, reservationRequest.getDate(), time, theme); + reservationRepository.save(reservation); return new ReservationResponse(reservation.getId(), reservation.getName(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue()); } public void deleteById(Long id) { - reservationDao.deleteById(id); + reservationRepository.deleteById(id); } public List findAll() { - return reservationDao.findAll().stream() - .map(it -> new ReservationResponse(it.getId(), it.getName(), it.getTheme().getName(), it.getDate(), it.getTime().getValue())) - .toList(); + return reservationRepository.findAll().stream().map(it -> new ReservationResponse(it.getId(), it.getName(), it.getTheme().getName(), it.getDate(), it.getTime().getValue())).toList(); } } diff --git a/src/main/java/roomescape/theme/Theme.java b/src/main/java/roomescape/theme/Theme.java index 430a6239c..1063c86de 100644 --- a/src/main/java/roomescape/theme/Theme.java +++ b/src/main/java/roomescape/theme/Theme.java @@ -1,6 +1,14 @@ package roomescape.theme; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity public class Theme { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String description; diff --git a/src/main/java/roomescape/theme/ThemeRepository.java b/src/main/java/roomescape/theme/ThemeRepository.java new file mode 100644 index 000000000..faddf8cca --- /dev/null +++ b/src/main/java/roomescape/theme/ThemeRepository.java @@ -0,0 +1,7 @@ +package roomescape.theme; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ThemeRepository extends JpaRepository { + +} diff --git a/src/main/java/roomescape/time/Time.java b/src/main/java/roomescape/time/Time.java index 008ed93cf..8f839f27c 100644 --- a/src/main/java/roomescape/time/Time.java +++ b/src/main/java/roomescape/time/Time.java @@ -1,7 +1,14 @@ package roomescape.time; +import jakarta.persistence.*; + +@Entity public class Time { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @Column(name = "time_value") private String value; public Time(Long id, String value) { diff --git a/src/main/java/roomescape/time/TimeRepository.java b/src/main/java/roomescape/time/TimeRepository.java new file mode 100644 index 000000000..3c4c3697a --- /dev/null +++ b/src/main/java/roomescape/time/TimeRepository.java @@ -0,0 +1,7 @@ +package roomescape.time; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TimeRepository extends JpaRepository { + +} diff --git a/src/main/java/roomescape/time/TimeService.java b/src/main/java/roomescape/time/TimeService.java index 7d0cb9b09..1dff591bc 100644 --- a/src/main/java/roomescape/time/TimeService.java +++ b/src/main/java/roomescape/time/TimeService.java @@ -2,23 +2,25 @@ import org.springframework.stereotype.Service; import roomescape.reservation.Reservation; -import roomescape.reservation.ReservationDao; +import roomescape.reservation.ReservationRepository; import java.util.List; @Service public class TimeService { - private TimeDao timeDao; - private ReservationDao reservationDao; - - public TimeService(TimeDao timeDao, ReservationDao reservationDao) { - this.timeDao = timeDao; - this.reservationDao = reservationDao; +// private TimeDao timeDao; +// private ReservationDao reservationDao; + private TimeRepository timeRepository; + private ReservationRepository reservationRepository; + + public TimeService(TimeRepository timeRepository, ReservationRepository reservationRepository) { + this.timeRepository = timeRepository; + this.reservationRepository = reservationRepository; } public List getAvailableTime(String date, Long themeId) { - List reservations = reservationDao.findByDateAndThemeId(date, themeId); - List