-
Notifications
You must be signed in to change notification settings - Fork 56
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] 한경준 미션 제출합니다. #42
base: hkjbrian
Are you sure you want to change the base?
Changes from all commits
22a3c65
10e758b
96dcea9
ec00aac
676db68
c2396a4
b9837ba
4a649d6
5ee087f
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,34 @@ | ||
package roomescape.auth; | ||
|
||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.SpringServletContainerInitializer; | ||
import org.springframework.web.servlet.HandlerInterceptor; | ||
import roomescape.member.Member; | ||
import roomescape.member.MemberDao; | ||
import roomescape.member.MemberService; | ||
|
||
@Component | ||
public class AdminInterceptor implements HandlerInterceptor { | ||
private final MemberDao memberDao; | ||
private final MemberService memberService; | ||
|
||
public AdminInterceptor(MemberDao memberDao, MemberService memberService) { | ||
this.memberDao = memberDao; | ||
this.memberService = memberService; | ||
} | ||
|
||
@Override | ||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { | ||
Long memberId = memberService.checkLogin(request).getId(); | ||
Member member = memberDao.findById(memberId); | ||
|
||
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,42 @@ | ||
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.stereotype.Controller; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import roomescape.member.LoginMember; | ||
import roomescape.member.MemberService; | ||
|
||
@Controller | ||
public class AuthController { | ||
MemberService memberService; | ||
|
||
public AuthController(MemberService memberService) { | ||
this.memberService = memberService; | ||
} | ||
|
||
@PostMapping("/login") | ||
public ResponseEntity<Void> tokenLogin(@RequestBody LoginRequest request, HttpServletResponse response) { | ||
|
||
String accessToken = memberService.loginByEmailAndPassword(request); | ||
|
||
Cookie cookie = new Cookie("token", accessToken); | ||
cookie.setHttpOnly(true); | ||
cookie.setPath("/"); | ||
response.addCookie(cookie); | ||
|
||
return ResponseEntity.ok().build(); | ||
} | ||
|
||
@GetMapping("/login/check") | ||
public ResponseEntity<LoginMember> loginCheck(HttpServletRequest request) { | ||
|
||
LoginMember loginMember = memberService.checkLogin(request); | ||
|
||
return ResponseEntity.ok().body(loginMember); | ||
} | ||
Comment on lines
+35
to
+41
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. 단계가 넘어가서 수정이 안된거 같지만 |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package roomescape.auth; | ||
|
||
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.member.MemberService; | ||
|
||
import java.util.List; | ||
|
||
@Configuration | ||
public class AuthWebConfiguration implements WebMvcConfigurer { | ||
|
||
private final LoginMemberArgumentResolver loginMemberArgumentResolver; | ||
private final AdminInterceptor adminInterceptor; | ||
|
||
public AuthWebConfiguration( | ||
LoginMemberArgumentResolver loginMemberArgumentResolver, | ||
AdminInterceptor adminInterceptor | ||
) { | ||
this.loginMemberArgumentResolver = loginMemberArgumentResolver; | ||
this.adminInterceptor = adminInterceptor; | ||
} | ||
|
||
@Override | ||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { | ||
resolvers.add(loginMemberArgumentResolver); | ||
} | ||
|
||
@Override | ||
public void addInterceptors(InterceptorRegistry registry) { | ||
registry.addInterceptor(adminInterceptor) | ||
.addPathPatterns("/admin"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package roomescape.auth; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
@Retention(RetentionPolicy.RUNTIME) | ||
@Target(ElementType.PARAMETER) | ||
public @interface Authentication { | ||
} | ||
Comment on lines
+8
to
+11
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. 이 부분은 궁금하네요 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package roomescape.auth; | ||
|
||
import io.jsonwebtoken.Jwts; | ||
import io.jsonwebtoken.security.Keys; | ||
import jakarta.servlet.http.Cookie; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
public class Extractor { | ||
|
||
public String extractToken(HttpServletRequest request) { | ||
Cookie[] cookies = request.getCookies(); | ||
|
||
for (Cookie cookie : cookies) { | ||
if (cookie.getName().equals("token")) { | ||
return cookie.getValue(); | ||
} | ||
} | ||
return ""; | ||
} | ||
|
||
public Long extractId(String token) { | ||
return Long.valueOf(Jwts.parserBuilder() | ||
.setSigningKey(Keys.hmacShaKeyFor("Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E=".getBytes())) | ||
.build() | ||
.parseClaimsJws(token) | ||
.getBody().getSubject()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package roomescape.auth; | ||
|
||
import jakarta.servlet.http.HttpServletRequest; | ||
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 roomescape.member.LoginMember; | ||
import roomescape.member.MemberService; | ||
|
||
import java.lang.annotation.Annotation; | ||
|
||
@Component | ||
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { | ||
private MemberService memberService; | ||
|
||
public LoginMemberArgumentResolver(MemberService memberService) { | ||
this.memberService = memberService; | ||
} | ||
|
||
@Override | ||
public boolean supportsParameter(MethodParameter parameter) { | ||
return parameter.hasParameterAnnotation(Annotation.class) | ||
&& parameter.getParameterType().equals(LoginMember.class); | ||
} | ||
|
||
@Override | ||
public Object resolveArgument(MethodParameter parameter, | ||
ModelAndViewContainer mavContainer, | ||
NativeWebRequest webRequest, | ||
WebDataBinderFactory binderFactory) throws Exception { | ||
|
||
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); | ||
return memberService.checkLogin(request); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package roomescape.auth; | ||
|
||
public record LoginRequest( | ||
String email, | ||
String password | ||
) { | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package roomescape.auth; | ||
|
||
import io.jsonwebtoken.Jwts; | ||
import io.jsonwebtoken.security.Keys; | ||
import org.springframework.stereotype.Component; | ||
import roomescape.member.Member; | ||
|
||
@Component | ||
public class TokenProvider { | ||
private String secretKey = "Yn2kjibddFAWtnPJ2AFlL8WXmohJMCvigQggaEypa5E="; | ||
|
||
Comment on lines
+10
to
+11
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. 직접적으로 토큰키를 명시하기 보다는 application.yaml 파일에 있는 내용을 가지고 오는건 어떨가요?? hint : @value 어노테이션 : ) |
||
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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,60 @@ | ||
package roomescape.member; | ||
|
||
import jakarta.servlet.http.Cookie; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import org.springframework.dao.DataAccessException; | ||
import org.springframework.stereotype.Service; | ||
import roomescape.auth.Extractor; | ||
import roomescape.auth.LoginRequest; | ||
import roomescape.auth.TokenProvider; | ||
|
||
import java.util.Arrays; | ||
|
||
@Service | ||
public class MemberService { | ||
private MemberDao memberDao; | ||
private final MemberDao memberDao; | ||
private final TokenProvider tokenProvider; | ||
private final Extractor extractor; | ||
|
||
public MemberService(MemberDao memberDao) { | ||
public MemberService(MemberDao memberDao, TokenProvider tokenProvider, Extractor extractor) { | ||
this.memberDao = memberDao; | ||
this.tokenProvider = tokenProvider; | ||
this.extractor = extractor; | ||
} | ||
|
||
public MemberResponse createMember(MemberRequest memberRequest) { | ||
public LoginMember 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 LoginMember(member.getId(), member.getName(), member.getEmail()); | ||
} | ||
|
||
public String loginByEmailAndPassword(LoginRequest request) { | ||
try { | ||
Member member = memberDao.findByEmailAndPassword(request.email(), request.password()); | ||
return tokenProvider.createToken(member); | ||
} catch (DataAccessException e) { | ||
throw new IllegalArgumentException("로그인 정보가 불일치 합니다."); | ||
} | ||
} | ||
|
||
public LoginMember checkLogin(HttpServletRequest request) { | ||
Arrays.stream(request.getCookies()) | ||
.filter(cookie -> cookie.getName().equals("token")) | ||
.findFirst() | ||
.map(Cookie::getValue) | ||
.orElseThrow(() -> new IllegalArgumentException("로그인이 필요합니다.")); | ||
|
||
String token = extractToken(request); | ||
Long memberId = extractId(token); | ||
|
||
Member member = memberDao.findById(memberId); | ||
return LoginMember.from(member); | ||
} | ||
|
||
private String extractToken(HttpServletRequest request) { | ||
return extractor.extractToken(request); | ||
} | ||
|
||
private Long extractId(String token) { | ||
return extractor.extractId(token); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,9 @@ | ||
package roomescape.reservation; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.DeleteMapping; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import org.springframework.web.bind.annotation.*; | ||
import roomescape.auth.Authentication; | ||
import roomescape.member.LoginMember; | ||
|
||
import java.net.URI; | ||
import java.util.List; | ||
|
@@ -26,11 +23,21 @@ 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) { | ||
public ResponseEntity create(@Authentication LoginMember loginMember ,@RequestBody ReservationRequest reservationRequest ) { | ||
|
||
if (reservationRequest.name() == null) { | ||
reservationRequest = new ReservationRequest( | ||
loginMember.getName(), | ||
reservationRequest.date(), | ||
reservationRequest.theme(), | ||
reservationRequest.time() | ||
); | ||
} | ||
|
||
if (reservationRequest.name() == null | ||
|| reservationRequest.date() == null | ||
|| reservationRequest.theme() == null | ||
|| reservationRequest.time() == null) { | ||
return ResponseEntity.badRequest().build(); | ||
} | ||
Comment on lines
+28
to
42
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); | ||
|
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.
MemberDao 는 데이터베이스와 직접적으로 소통하는 객채입니다.
Interceptor 클래스에서 MemberDao를 직접 소환하는게 아닌 memberService만 소환하여
member 객체를 찾는 방법은 어떨까요? : )