Skip to content
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

Open
wants to merge 9 commits into
base: hkjbrian
Choose a base branch
from
34 changes: 34 additions & 0 deletions src/main/java/roomescape/auth/AdminInterceptor.java
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);

Comment on lines +13 to +26

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 객체를 찾는 방법은 어떨까요? : )

if (member == null || !member.getRole().equals("ADMIN")) {
response.setStatus(401);
return false;
}

return true;
}
}
42 changes: 42 additions & 0 deletions src/main/java/roomescape/auth/AuthController.java
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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

단계가 넘어가서 수정이 안된거 같지만
경준님께서 만드신 @authentication 을 활용해보는게 어떨가요??

}
35 changes: 35 additions & 0 deletions src/main/java/roomescape/auth/AuthWebConfiguration.java
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");
}
}
11 changes: 11 additions & 0 deletions src/main/java/roomescape/auth/Authentication.java
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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 궁금하네요
@annotation 으로 만들지 않고 @interface 로 만드신 이유가 있나요?

30 changes: 30 additions & 0 deletions src/main/java/roomescape/auth/Extractor.java
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());
}
}
38 changes: 38 additions & 0 deletions src/main/java/roomescape/auth/LoginMemberArgumentResolver.java
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);
}
}
7 changes: 7 additions & 0 deletions src/main/java/roomescape/auth/LoginRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package roomescape.auth;

public record LoginRequest(
String email,
String password
) {
}
21 changes: 21 additions & 0 deletions src/main/java/roomescape/auth/TokenProvider.java
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

Choose a reason for hiding this comment

The 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,16 +1,20 @@
package roomescape.member;

public class MemberResponse {
public class LoginMember {
private Long id;
private String name;
private String email;

public MemberResponse(Long id, String name, String email) {
public LoginMember(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}

public static LoginMember from(Member member) {
return new LoginMember(member.getId(), member.getName(), member.getEmail());
}

public Long getId() {
return id;
}
Expand Down
4 changes: 1 addition & 3 deletions src/main/java/roomescape/member/MemberController.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -21,7 +19,7 @@ public MemberController(MemberService memberService) {

@PostMapping("/members")
public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) {
MemberResponse member = memberService.createMember(memberRequest);
LoginMember member = memberService.createMember(memberRequest);
return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member);
}

Expand Down
6 changes: 3 additions & 3 deletions src/main/java/roomescape/member/MemberDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ public Member findByEmailAndPassword(String email, String password) {
);
}

public Member findByName(String name) {
public Member findById(Long id) {
return jdbcTemplate.queryForObject(
"SELECT id, name, email, role FROM member WHERE name = ?",
"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")
),
name
id
);
}
}
51 changes: 47 additions & 4 deletions src/main/java/roomescape/member/MemberService.java
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);
}
}
29 changes: 18 additions & 11 deletions src/main/java/roomescape/reservation/ReservationController.java
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;
Expand All @@ -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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

순서를 바꾸고 코드를 작게 수정하면
중복되는 코드없이 더 효율적으로 작성할 수 있을거 같아요 :)

ReservationResponse reservation = reservationService.save(reservationRequest);
Expand Down
Loading