-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Spring MVC] 인상진 미션 제출합니다. #83
base: sangjin6439
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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"); | ||
} | ||
Comment on lines
+27
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 누가 로그인을 하더라도 LoginMember의 객체에서 role 필드는 항상 "USER"인 것인가요?? |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. EOL(파일 마지막 줄 공백)을 지키면 좋을 거 같습니다! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package roomescape.global; | ||
|
||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.method.support.HandlerMethodArgumentResolver; | ||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
||
|
||
import java.util.List; | ||
|
||
@Configuration | ||
public class WebConfig implements WebMvcConfigurer { | ||
|
||
private final LoginMemberArgumentResolver loginMemberArgumentResolver; | ||
private final RoleHandler roleHandler; | ||
|
||
public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver, final RoleHandler roleHandler) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
this.loginMemberArgumentResolver = loginMemberArgumentResolver; | ||
this.roleHandler = roleHandler; | ||
} | ||
|
||
@Override | ||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { | ||
resolvers.add(loginMemberArgumentResolver); | ||
} | ||
|
||
@Override | ||
public void addInterceptors(InterceptorRegistry registry) { | ||
registry.addInterceptor(roleHandler) | ||
.addPathPatterns("/admin/**"); | ||
} | ||
Comment on lines
+27
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ant 경로 패턴을 사용하셨는데, 이 패턴을 사용하면 어떤 단점이 있을까요? |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,16 +2,60 @@ | |
|
||
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.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) { | ||
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() , member.getRole()); | ||
} | ||
|
||
public MemberResponse findByToken(String token) { | ||
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(), member.getRole()); | ||
} | ||
Comment on lines
+31
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기도 토큰 서명을 위한 비밀 키를 상수로 빼면 더 좋을 것 같아요! (다른 코드에서는 빼셔서 말씀드려요!) |
||
|
||
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; | ||
} | ||
Comment on lines
+50
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 항상 만들어진 token의 role 필드 claim은 USER인건가요? |
||
|
||
public void createCookie(final HttpServletResponse response, final String token) { | ||
Cookie cookie = new Cookie("token", token); | ||
cookie.setHttpOnly(true); | ||
cookie.setPath("/"); | ||
response.addCookie(cookie); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<ReservationResponse> 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(); | ||
} | ||
Comment on lines
-29
to
-35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
ReservationResponse reservation = reservationService.save(reservationRequest); | ||
public ResponseEntity<ReservationResponse> createReservation(@RequestBody ReservationRequest reservationRequest, LoginMember member) { | ||
|
||
ReservationResponse reservation = reservationService.save(member, reservationRequest); | ||
return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
패키지 구조를
global
로 설정하신 이유가 있을까요?