From cc3abbc0dc4eaacd8489e42d51323cf1df6ee188 Mon Sep 17 00:00:00 2001 From: mereorfish Date: Thu, 20 Jun 2024 20:13:43 +0900 Subject: [PATCH 01/16] =?UTF-8?q?feat:=201=EB=8B=A8=EA=B3=84=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/api/JwtDecoder.java | 25 +++++++++ src/main/java/roomescape/api/JwtProvider.java | 19 +++++++ .../roomescape/member/MemberController.java | 23 +++++++- .../java/roomescape/member/MemberDao.java | 13 +++++ .../java/roomescape/member/MemberRequest.java | 35 +++++++++---- .../roomescape/member/MemberResponse.java | 52 +++++++++++++------ .../java/roomescape/member/MemberService.java | 28 +++++++++- src/main/resources/application.properties | 2 +- src/main/resources/{templates => }/index.html | 4 +- src/test/java/roomescape/MissionStepTest.java | 11 +++- 10 files changed, 178 insertions(+), 34 deletions(-) create mode 100644 src/main/java/roomescape/api/JwtDecoder.java create mode 100644 src/main/java/roomescape/api/JwtProvider.java rename src/main/resources/{templates => }/index.html (95%) diff --git a/src/main/java/roomescape/api/JwtDecoder.java b/src/main/java/roomescape/api/JwtDecoder.java new file mode 100644 index 00000000..effdd357 --- /dev/null +++ b/src/main/java/roomescape/api/JwtDecoder.java @@ -0,0 +1,25 @@ +package roomescape.api; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.Cookie; + +public class JwtDecoder { + public static Long decodeJwtToken(String token) { + Long memberId = Long.valueOf(Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor("Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E=".getBytes())) + .build() + .parseClaimsJws(token) + .getBody().getSubject()); + return memberId; + } + + public static String extractTokenFromCookie(Cookie[] cookies) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals("token")) { + return cookie.getValue(); + } + } + return ""; + } +} diff --git a/src/main/java/roomescape/api/JwtProvider.java b/src/main/java/roomescape/api/JwtProvider.java new file mode 100644 index 00000000..f4ae09d1 --- /dev/null +++ b/src/main/java/roomescape/api/JwtProvider.java @@ -0,0 +1,19 @@ +package roomescape.api; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; +import roomescape.member.Member; + +public class JwtProvider { + public static String createToken(Member member, String secretKey){ + return Jwts.builder() + .setSubject(member.getId().toString()) + .claim("name", member.getName()) + .claim("role", member.getRole()) + .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) + .compact(); + } +} diff --git a/src/main/java/roomescape/member/MemberController.java b/src/main/java/roomescape/member/MemberController.java index 881ae5e0..b279bda7 100644 --- a/src/main/java/roomescape/member/MemberController.java +++ b/src/main/java/roomescape/member/MemberController.java @@ -8,6 +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.api.JwtDecoder; import java.net.URI; @@ -20,11 +21,29 @@ public MemberController(MemberService memberService) { } @PostMapping("/members") - public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { - MemberResponse member = memberService.createMember(memberRequest); + public ResponseEntity createMember(@RequestBody MemberRequest.Create memberRequest) { + MemberResponse.Create member = memberService.createMember(memberRequest); return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member); } + @PostMapping("/login") + public ResponseEntity login(@RequestBody MemberRequest.Login request, HttpServletResponse response) { + String token = memberService.loginMember(request); + Cookie cookie = new Cookie("token", token); + cookie.setHttpOnly(true); + cookie.setPath("/"); + response.addCookie(cookie); + return ResponseEntity.ok().build(); + } + + @GetMapping("/login/check") + public ResponseEntity checkLogin(HttpServletRequest request) { + Cookie[] cookies = request.getCookies(); + String token = JwtDecoder.extractTokenFromCookie(cookies); + MemberResponse.Check response = memberService.checkMember(token); + return ResponseEntity.ok(response); + } + @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 81f77f4c..114fd342 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/MemberRequest.java b/src/main/java/roomescape/member/MemberRequest.java index cafb79f1..5fb1f2a8 100644 --- a/src/main/java/roomescape/member/MemberRequest.java +++ b/src/main/java/roomescape/member/MemberRequest.java @@ -1,19 +1,34 @@ package roomescape.member; public class MemberRequest { - private String name; - private String email; - private String password; + public static class Create { + private String name; + private String email; + private String password; - public String getName() { - return name; - } + public String getName() { + return name; + } + + public String getEmail() { + return email; + } - public String getEmail() { - return email; + public String getPassword() { + return password; + } } - public String getPassword() { - return password; + public static class Login { + private String email; + private String password; + + public String getEmail() { + return email; + } + + public String getPassword() { + return password; + } } } diff --git a/src/main/java/roomescape/member/MemberResponse.java b/src/main/java/roomescape/member/MemberResponse.java index b9fa3b97..724bdfb9 100644 --- a/src/main/java/roomescape/member/MemberResponse.java +++ b/src/main/java/roomescape/member/MemberResponse.java @@ -1,25 +1,45 @@ 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 static class Create { + private Long id; + private String name; + private String email; + + public Create(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 getName() { - return name; + public String getEmail() { + return email; + } } - public String getEmail() { - return email; + public static class Check { + private String name; + + public Check(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static Check from(Member member) { + return new Check(member.getName()); + } } + } diff --git a/src/main/java/roomescape/member/MemberService.java b/src/main/java/roomescape/member/MemberService.java index ccaa8cba..9e630769 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 org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import roomescape.api.JwtDecoder; +import roomescape.api.JwtProvider; @Service public class MemberService { + + @Value("${roomescape.auth.jwt.secret}") + private String secret; + private MemberDao memberDao; public MemberService(MemberDao memberDao) { this.memberDao = memberDao; } - public MemberResponse createMember(MemberRequest memberRequest) { + public MemberResponse.Create createMember(MemberRequest.Create 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.Create(member.getId(), member.getName(), member.getEmail()); + } + + public String loginMember(MemberRequest.Login request) { + Member member = memberDao.findByEmailAndPassword(request.getEmail(), request.getPassword()); + if(member == null){ + return null; + } + return JwtProvider.createToken(member, secret); + } + + public MemberResponse.Check checkMember(String token) { + Long memberId = JwtDecoder.decodeJwtToken(token); + Member member = memberDao.findById(memberId); + if(member == null) { + return null; + } + return MemberResponse.Check.from(member); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a0f33bba..612d9890 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= \ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/index.html similarity index 95% rename from src/main/resources/templates/index.html rename to src/main/resources/index.html index 1341aff4..520946c9 100644 --- a/src/main/resources/templates/index.html +++ b/src/main/resources/index.html @@ -11,7 +11,7 @@