Skip to content

Commit 1831139

Browse files
committed
feat: 로그인 시 이름 없을 때 로그인정보로 예약가능하도록 수정 (2단계)
- ArgumentResolver를 이용한 LoginMember 바인딩 - TokenUtil 분리
1 parent 864d9fe commit 1831139

9 files changed

+228
-114
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package roomescape.config;
2+
3+
import java.util.List;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
6+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
7+
import roomescape.member.LoginMemberArgumentResolver;
8+
9+
@Configuration
10+
public class WebConfig implements WebMvcConfigurer {
11+
12+
private final LoginMemberArgumentResolver loginMemberArgumentResolver;
13+
14+
public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver) {
15+
this.loginMemberArgumentResolver = loginMemberArgumentResolver;
16+
}
17+
18+
@Override
19+
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> resolvers) {
20+
resolvers.add(this.loginMemberArgumentResolver);
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package roomescape.member;
2+
3+
public class LoginMember extends Member {
4+
5+
public LoginMember(final Long id, final String name, final String email, final String role) {
6+
super(id, name, email, role);
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package roomescape.member;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
import org.springframework.core.MethodParameter;
5+
import org.springframework.stereotype.Component;
6+
import org.springframework.web.bind.support.WebDataBinderFactory;
7+
import org.springframework.web.context.request.NativeWebRequest;
8+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
9+
import org.springframework.web.method.support.ModelAndViewContainer;
10+
11+
@Component
12+
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
13+
14+
private final MemberService memberService;
15+
private final TokenUtil tokenUtil;
16+
17+
public LoginMemberArgumentResolver(MemberService memberService, TokenUtil tokenUtil) {
18+
this.memberService = memberService;
19+
this.tokenUtil = tokenUtil;
20+
}
21+
22+
@Override
23+
public boolean supportsParameter(final MethodParameter parameter) {
24+
return parameter.getParameterType().equals(LoginMember.class);
25+
26+
}
27+
28+
@Override
29+
public Object resolveArgument(
30+
final MethodParameter parameter,
31+
final ModelAndViewContainer mavContainer,
32+
final NativeWebRequest webRequest,
33+
final WebDataBinderFactory binderFactory)
34+
throws Exception {
35+
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
36+
Long memberId = tokenUtil.getMemberId(request.getCookies());
37+
38+
Member member = memberService.find(memberId);
39+
return new LoginMember(member.getId(), member.getName(), member.getEmail(), member.getRole());
40+
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
package roomescape.member;
22

3-
import io.jsonwebtoken.Jwts;
4-
import io.jsonwebtoken.SignatureAlgorithm;
5-
import io.jsonwebtoken.security.Keys;
63
import jakarta.servlet.http.Cookie;
74
import jakarta.servlet.http.HttpServletRequest;
85
import jakarta.servlet.http.HttpServletResponse;
9-
import java.security.Key;
10-
import java.util.Date;
116
import java.util.Map;
127
import org.springframework.http.ResponseEntity;
138
import org.springframework.web.bind.annotation.GetMapping;
@@ -20,89 +15,55 @@
2015
@RestController
2116
public class MemberController {
2217

23-
private final MemberDao memberDao;
24-
private MemberService memberService;
25-
26-
private Key key;
27-
28-
public MemberController(MemberService memberService, MemberDao memberDao) {
29-
this.memberService = memberService;
30-
this.key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
31-
this.memberDao = memberDao;
32-
}
33-
34-
@PostMapping("/members")
35-
public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) {
36-
MemberResponse member = memberService.createMember(memberRequest);
37-
return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member);
38-
}
39-
40-
@PostMapping("/login")
41-
public ResponseEntity<Void> login(@RequestBody Map<String, String> body) {
42-
// HttpServletRequest 가 Request 객체가 아닌가? 일단 Map으로 대체
43-
String email = body.get("email");
44-
String password = body.get("password");
45-
Date now = new Date();
46-
47-
// duration 1시간으로 가정
48-
int durationSecond = 60 * 60;
49-
Date expirationDate = new Date(now.getTime() + 1000L * durationSecond);
50-
51-
// TODO: member가 존재하지 않을때의 처리
52-
Member member = this.memberDao.findByEmailAndPassword(email, password);
53-
54-
String token = Jwts.builder()
55-
.setSubject(member.getId().toString())
56-
.claim("name", member.getName())
57-
.claim("role", member.getName())
58-
.signWith(this.key)
59-
.setIssuedAt(now)
60-
.setExpiration(expirationDate)
61-
.compact();
62-
63-
64-
// TODO: 쿠키를 header 에 정상적으로 넣도록 수정
65-
Cookie cookie = new Cookie("token", token);
66-
cookie.setHttpOnly(true);
67-
cookie.setPath("/");
68-
69-
return ResponseEntity.ok().header("Set-Cookie", "token=" + token + ";").build();
70-
}
71-
72-
@GetMapping("/login/check")
73-
public ResponseEntity checkLogin(HttpServletRequest request) {
74-
Cookie[] cookies = request.getCookies();
75-
76-
String token = this.extractTokenFromCookie(cookies);
77-
78-
Long memberId = Long.valueOf(Jwts.parserBuilder()
79-
.setSigningKey(this.key)
80-
.build()
81-
.parseClaimsJws(token)
82-
.getBody().getSubject());
83-
84-
Member member = this.memberDao.findById(memberId);
85-
86-
return ResponseEntity.ok().body(Map.of("name", member.getName()));
87-
}
88-
89-
private String extractTokenFromCookie(Cookie[] cookies) {
90-
for (Cookie cookie : cookies) {
91-
if (cookie.getName().equals("token")) {
92-
return cookie.getValue();
93-
}
94-
}
95-
96-
return "";
97-
}
98-
99-
@PostMapping("/logout")
100-
public ResponseEntity logout(HttpServletResponse response) {
101-
Cookie cookie = new Cookie("token", "");
102-
cookie.setHttpOnly(true);
103-
cookie.setPath("/");
104-
cookie.setMaxAge(0);
105-
response.addCookie(cookie);
106-
return ResponseEntity.ok().build();
107-
}
18+
private final MemberDao memberDao;
19+
private final TokenUtil tokenUtil;
20+
private MemberService memberService;
21+
22+
public MemberController(MemberService memberService, MemberDao memberDao, TokenUtil tokenUtil) {
23+
this.memberService = memberService;
24+
this.memberDao = memberDao;
25+
this.tokenUtil = tokenUtil;
26+
}
27+
28+
@PostMapping("/members")
29+
public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) {
30+
MemberResponse member = memberService.createMember(memberRequest);
31+
return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member);
32+
}
33+
34+
@PostMapping("/login")
35+
public ResponseEntity<Void> login(@RequestBody Map<String, String> body) {
36+
// HttpServletRequest 가 Request 객체가 아닌가? 일단 Map으로 대체
37+
String email = body.get("email");
38+
String password = body.get("password");
39+
40+
// TODO: member가 존재하지 않을때의 처리
41+
Member member = memberDao.findByEmailAndPassword(email, password);
42+
String token = tokenUtil.generate(member);
43+
44+
// TODO: 쿠키를 header 에 정상적으로 넣도록 수정
45+
Cookie cookie = new Cookie("token", token);
46+
cookie.setHttpOnly(true);
47+
cookie.setPath("/");
48+
49+
return ResponseEntity.ok().header("Set-Cookie", "token=" + token + ";").build();
50+
}
51+
52+
@GetMapping("/login/check")
53+
public ResponseEntity<Map<String, String>> checkLogin(HttpServletRequest request) {
54+
Long memberId = tokenUtil.getMemberId(request.getCookies());
55+
Member member = memberDao.findById(memberId);
56+
57+
return ResponseEntity.ok().body(Map.of("name", member.getName()));
58+
}
59+
60+
@PostMapping("/logout")
61+
public ResponseEntity<Void> logout(HttpServletResponse response) {
62+
Cookie cookie = new Cookie("token", "");
63+
cookie.setHttpOnly(true);
64+
cookie.setPath("/");
65+
cookie.setMaxAge(0);
66+
response.addCookie(cookie);
67+
return ResponseEntity.ok().build();
68+
}
10869
}

src/main/java/roomescape/member/MemberService.java

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ public MemberService(MemberDao memberDao) {
1010
this.memberDao = memberDao;
1111
}
1212

13+
public Member find(Long memberId) {
14+
return this.memberDao.findById(memberId);
15+
}
16+
1317
public MemberResponse createMember(MemberRequest memberRequest) {
1418
Member member = memberDao.save(new Member(memberRequest.getName(), memberRequest.getEmail(), memberRequest.getPassword(), "USER"));
1519
return new MemberResponse(member.getId(), member.getName(), member.getEmail());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package roomescape.member;
2+
3+
import io.jsonwebtoken.Jwts;
4+
import io.jsonwebtoken.SignatureAlgorithm;
5+
import io.jsonwebtoken.security.Keys;
6+
import jakarta.servlet.http.Cookie;
7+
import java.security.Key;
8+
import java.util.Date;
9+
import org.springframework.stereotype.Component;
10+
11+
12+
@Component
13+
public class TokenUtil {
14+
15+
private Key key;
16+
17+
public TokenUtil() {
18+
this.key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
19+
}
20+
21+
public String generate(final Member member) {
22+
Date now = new Date();
23+
24+
// duration 1시간으로 가정
25+
int durationSecond = 60 * 60;
26+
Date expirationDate = new Date(now.getTime() + 1000L * durationSecond);
27+
28+
return Jwts.builder()
29+
.setSubject(member.getId().toString())
30+
.claim("name", member.getName())
31+
.claim("role", member.getName())
32+
.signWith(this.key)
33+
.setIssuedAt(now)
34+
.setExpiration(expirationDate)
35+
.compact();
36+
}
37+
38+
public Long getMemberId(final Cookie[] cookies) {
39+
40+
String token = this.extractTokenFromCookie(cookies);
41+
42+
return Long.valueOf(Jwts.parserBuilder()
43+
.setSigningKey(this.key)
44+
.build()
45+
.parseClaimsJws(token)
46+
.getBody().getSubject());
47+
}
48+
49+
private String extractTokenFromCookie(Cookie[] cookies) {
50+
for (Cookie cookie : cookies) {
51+
if (cookie.getName().equals("token")) {
52+
return cookie.getValue();
53+
}
54+
}
55+
56+
return "";
57+
}
58+
}

src/main/java/roomescape/reservation/ReservationController.java

+28-24
Original file line numberDiff line numberDiff line change
@@ -10,37 +10,41 @@
1010

1111
import java.net.URI;
1212
import java.util.List;
13+
import roomescape.member.LoginMember;
14+
import roomescape.member.Member;
1315

1416
@RestController
1517
public class ReservationController {
1618

17-
private final ReservationService reservationService;
19+
private final ReservationService reservationService;
1820

19-
public ReservationController(ReservationService reservationService) {
20-
this.reservationService = reservationService;
21-
}
21+
public ReservationController(ReservationService reservationService) {
22+
this.reservationService = reservationService;
23+
}
2224

23-
@GetMapping("/reservations")
24-
public List<ReservationResponse> list() {
25-
return reservationService.findAll();
26-
}
25+
@GetMapping("/reservations")
26+
public List<ReservationResponse> list() {
27+
return reservationService.findAll();
28+
}
2729

28-
@PostMapping("/reservations")
29-
public ResponseEntity create(@RequestBody ReservationRequest reservationRequest) {
30-
if (reservationRequest.getName() == null
31-
|| reservationRequest.getDate() == null
32-
|| reservationRequest.getTheme() == null
33-
|| reservationRequest.getTime() == null) {
34-
return ResponseEntity.badRequest().build();
35-
}
36-
ReservationResponse reservation = reservationService.save(reservationRequest);
37-
38-
return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation);
30+
@PostMapping("/reservations")
31+
public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, LoginMember member) {
32+
if ((member == null && reservationRequest.getName() == null)
33+
|| reservationRequest.getDate() == null
34+
|| reservationRequest.getTheme() == null
35+
|| reservationRequest.getTime() == null) {
36+
return ResponseEntity.badRequest().build();
3937
}
4038

41-
@DeleteMapping("/reservations/{id}")
42-
public ResponseEntity delete(@PathVariable Long id) {
43-
reservationService.deleteById(id);
44-
return ResponseEntity.noContent().build();
45-
}
39+
ReservationResponse reservation = reservationService.save(member, reservationRequest);
40+
41+
return ResponseEntity.created(URI.create("/reservations/" + reservation.getId()))
42+
.body(reservation);
43+
}
44+
45+
@DeleteMapping("/reservations/{id}")
46+
public ResponseEntity delete(@PathVariable Long id) {
47+
reservationService.deleteById(id);
48+
return ResponseEntity.noContent().build();
49+
}
4650
}

src/main/java/roomescape/reservation/ReservationRequest.java

+4
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,8 @@ public Long getTheme() {
2121
public Long getTime() {
2222
return time;
2323
}
24+
25+
public void setName(final String name) {
26+
this.name = name;
27+
}
2428
}

src/main/java/roomescape/reservation/ReservationService.java

+11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import org.springframework.stereotype.Service;
44

55
import java.util.List;
6+
import roomescape.member.LoginMember;
7+
import roomescape.member.Member;
68

79
@Service
810
public class ReservationService {
@@ -12,6 +14,15 @@ public ReservationService(ReservationDao reservationDao) {
1214
this.reservationDao = reservationDao;
1315
}
1416

17+
public ReservationResponse save(LoginMember member, ReservationRequest reservationRequest) {
18+
if (reservationRequest.getName() == null) {
19+
reservationRequest.setName(member.getName());
20+
}
21+
Reservation reservation = reservationDao.save(reservationRequest);
22+
23+
return new ReservationResponse(reservation.getId(), reservationRequest.getName(), reservation.getTheme().getName(), reservation.getDate(), reservation.getTime().getValue());
24+
}
25+
1526
public ReservationResponse save(ReservationRequest reservationRequest) {
1627
Reservation reservation = reservationDao.save(reservationRequest);
1728

0 commit comments

Comments
 (0)