Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/main/java/roomescape/PageController.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package roomescape;

import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import roomescape.member.LoginMember;
import roomescape.member.Member;

@Controller
public class PageController {
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/roomescape/config/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package roomescape.config;

import java.util.List;
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.LoginMemberArgumentResolver;
import roomescape.member.LoginMemberInterceptor;

@Configuration
public class WebConfig implements WebMvcConfigurer {

private final LoginMemberArgumentResolver loginMemberArgumentResolver;
private final LoginMemberInterceptor loginMemberInterceptor;

public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver, LoginMemberInterceptor loginMemberInterceptor) {
this.loginMemberArgumentResolver = loginMemberArgumentResolver;
this.loginMemberInterceptor = loginMemberInterceptor;
}

@Override
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(loginMemberArgumentResolver);
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginMemberInterceptor).addPathPatterns("/admin");
}
}
8 changes: 8 additions & 0 deletions src/main/java/roomescape/member/LoginMember.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package roomescape.member;

public class LoginMember extends Member {

public LoginMember(final Long id, final String name, final String email, final String role) {
super(id, name, email, role);
}
}
42 changes: 42 additions & 0 deletions src/main/java/roomescape/member/LoginMemberArgumentResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package roomescape.member;

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;

@Component
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {

private final MemberService memberService;
private final TokenUtil tokenUtil;

public LoginMemberArgumentResolver(MemberService memberService, TokenUtil tokenUtil) {
this.memberService = memberService;
this.tokenUtil = tokenUtil;
}

@Override
public boolean supportsParameter(final MethodParameter parameter) {
return parameter.getParameterType().equals(LoginMember.class);

}

@Override
public Object resolveArgument(
final MethodParameter parameter,
final ModelAndViewContainer mavContainer,
final NativeWebRequest webRequest,
final WebDataBinderFactory binderFactory)
throws Exception {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
Long memberId = tokenUtil.getMemberId(request.getCookies());

Member member = memberService.find(memberId);
return new LoginMember(member.getId(), member.getName(), member.getEmail(), member.getRole());

}
}
31 changes: 31 additions & 0 deletions src/main/java/roomescape/member/LoginMemberInterceptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package roomescape.member;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
public class LoginMemberInterceptor implements HandlerInterceptor {

private final TokenUtil tokenUtil;
private final MemberService memberService;

public LoginMemberInterceptor(TokenUtil tokenUtil, MemberService memberService) {
this.tokenUtil = tokenUtil;
this.memberService = memberService;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Long memberId = this.tokenUtil.getMemberId(request.getCookies());

Member member = this.memberService.find(memberId);

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

return true;
}
}
74 changes: 53 additions & 21 deletions src/main/java/roomescape/member/MemberController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Map;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -13,25 +14,56 @@

@RestController
public class MemberController {
private MemberService memberService;

public MemberController(MemberService memberService) {
this.memberService = memberService;
}

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

@PostMapping("/logout")
public ResponseEntity logout(HttpServletResponse response) {
Cookie cookie = new Cookie("token", "");
cookie.setHttpOnly(true);
cookie.setPath("/");
cookie.setMaxAge(0);
response.addCookie(cookie);
return ResponseEntity.ok().build();
}

private final MemberDao memberDao;
private final TokenUtil tokenUtil;
private MemberService memberService;

public MemberController(MemberService memberService, MemberDao memberDao, TokenUtil tokenUtil) {
this.memberService = memberService;
this.memberDao = memberDao;
this.tokenUtil = tokenUtil;
}

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

@PostMapping("/login")
public ResponseEntity<Void> login(@RequestBody Map<String, String> body) {
// HttpServletRequest 가 Request 객체가 아닌가? 일단 Map으로 대체
String email = body.get("email");
String password = body.get("password");

// TODO: member가 존재하지 않을때의 처리
Member member = memberDao.findByEmailAndPassword(email, password);
String token = tokenUtil.generate(member);

// TODO: 쿠키를 header 에 정상적으로 넣도록 수정
Cookie cookie = new Cookie("token", token);
cookie.setHttpOnly(true);
cookie.setPath("/");

return ResponseEntity.ok().header("Set-Cookie", "token=" + token + ";").build();
}

@GetMapping("/login/check")
public ResponseEntity<Map<String, String>> checkLogin(HttpServletRequest request) {
Long memberId = tokenUtil.getMemberId(request.getCookies());
Member member = memberDao.findById(memberId);

return ResponseEntity.ok().body(Map.of("name", member.getName()));
}

@PostMapping("/logout")
public ResponseEntity<Void> logout(HttpServletResponse response) {
Cookie cookie = new Cookie("token", "");
cookie.setHttpOnly(true);
cookie.setPath("/");
cookie.setMaxAge(0);
response.addCookie(cookie);
return ResponseEntity.ok().build();
}
}
12 changes: 12 additions & 0 deletions src/main/java/roomescape/member/MemberDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,16 @@ public Member findByName(String name) {
name
);
}

public Member findById(final Long memberId) {
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")
),
memberId
);
}
}
4 changes: 4 additions & 0 deletions src/main/java/roomescape/member/MemberService.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ public MemberService(MemberDao memberDao) {
this.memberDao = memberDao;
}

public Member find(Long memberId) {
return this.memberDao.findById(memberId);
}

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());
Expand Down
58 changes: 58 additions & 0 deletions src/main/java/roomescape/member/TokenUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package roomescape.member;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.Cookie;
import java.security.Key;
import java.util.Date;
import org.springframework.stereotype.Component;


@Component
public class TokenUtil {

private Key key;

public TokenUtil() {
this.key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
}

public String generate(final Member member) {
Date now = new Date();

// duration 1시간으로 가정
int durationSecond = 60 * 60;
Date expirationDate = new Date(now.getTime() + 1000L * durationSecond);

return Jwts.builder()
.setSubject(member.getId().toString())
.claim("name", member.getName())
.claim("role", member.getName())
.signWith(this.key)
.setIssuedAt(now)
.setExpiration(expirationDate)
.compact();
}

public Long getMemberId(final Cookie[] cookies) {

String token = this.extractTokenFromCookie(cookies);

return Long.valueOf(Jwts.parserBuilder()
.setSigningKey(this.key)
.build()
.parseClaimsJws(token)
.getBody().getSubject());
}

private String extractTokenFromCookie(Cookie[] cookies) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals("token")) {
return cookie.getValue();
}
}

return "";
}
}
52 changes: 28 additions & 24 deletions src/main/java/roomescape/reservation/ReservationController.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,41 @@

import java.net.URI;
import java.util.List;
import roomescape.member.LoginMember;
import roomescape.member.Member;

@RestController
public class ReservationController {

private final ReservationService reservationService;
private final ReservationService reservationService;

public ReservationController(ReservationService reservationService) {
this.reservationService = reservationService;
}
public ReservationController(ReservationService reservationService) {
this.reservationService = reservationService;
}

@GetMapping("/reservations")
public List<ReservationResponse> list() {
return reservationService.findAll();
}
@GetMapping("/reservations")
public List<ReservationResponse> list() {
return reservationService.findAll();
}

@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();
}
ReservationResponse reservation = reservationService.save(reservationRequest);

return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation);
@PostMapping("/reservations")
public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, LoginMember member) {
if ((member == null && reservationRequest.getName() == null)
|| reservationRequest.getDate() == null
|| reservationRequest.getTheme() == null
|| reservationRequest.getTime() == null) {
return ResponseEntity.badRequest().build();
}

@DeleteMapping("/reservations/{id}")
public ResponseEntity delete(@PathVariable Long id) {
reservationService.deleteById(id);
return ResponseEntity.noContent().build();
}
ReservationResponse reservation = reservationService.save(member, reservationRequest);

return ResponseEntity.created(URI.create("/reservations/" + reservation.getId()))
.body(reservation);
}

@DeleteMapping("/reservations/{id}")
public ResponseEntity delete(@PathVariable Long id) {
reservationService.deleteById(id);
return ResponseEntity.noContent().build();
}
}
4 changes: 4 additions & 0 deletions src/main/java/roomescape/reservation/ReservationRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ public Long getTheme() {
public Long getTime() {
return time;
}

public void setName(final String name) {
this.name = name;
}
}
Loading