Skip to content

Commit ef95f4a

Browse files
committed
Merge remote-tracking branch 'origin/Core' into Core
2 parents 928343c + fae54bf commit ef95f4a

13 files changed

+328
-2
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package roomescape.authentication;
2+
3+
import jakarta.servlet.http.Cookie;
4+
import jakarta.servlet.http.HttpServletRequest;
5+
import jakarta.servlet.http.HttpServletResponse;
6+
import org.springframework.stereotype.Component;
7+
import org.springframework.web.servlet.HandlerInterceptor;
8+
import roomescape.exception.AuthorizationException;
9+
import roomescape.domain.member.Member;
10+
11+
import java.util.Arrays;
12+
13+
@Component
14+
public class AdminAccessInterceptor implements HandlerInterceptor {
15+
private final AuthService authService;
16+
17+
public AdminAccessInterceptor(AuthService authService) {
18+
this.authService = authService;
19+
}
20+
21+
@Override
22+
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
23+
String token = extractTokenFromCookies(request.getCookies());
24+
25+
String email = authService.getEmailFromToken(token);
26+
Member member = authService.findLoginMemberByEmail(email);
27+
28+
if (member == null) {
29+
response.setStatus(401);
30+
return false;
31+
}
32+
33+
if (!"ADMIN".equals(member.getRole())) {
34+
response.setStatus(401);
35+
response.getWriter().write("권한이 없습니다.");
36+
return false;
37+
}
38+
39+
return true;
40+
}
41+
42+
private String extractTokenFromCookies(Cookie[] cookies) {
43+
if (cookies == null) {
44+
throw new AuthorizationException("쿠키가 존재하지 않습니다.");
45+
}
46+
47+
return Arrays.stream(cookies)
48+
.filter(cookie -> "token".equals(cookie.getName()))
49+
.findFirst()
50+
.map(Cookie::getValue)
51+
.orElseThrow(() -> new AuthorizationException("토큰 쿠키가 없습니다."));
52+
}
53+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package roomescape.authentication;
2+
3+
import org.springframework.stereotype.Service;
4+
import roomescape.domain.token.TokenRequest;
5+
import roomescape.domain.token.TokenResponse;
6+
import roomescape.exception.AuthorizationException;
7+
import roomescape.domain.member.Member;
8+
import roomescape.domain.member.MemberRepository;
9+
10+
@Service
11+
public class AuthService {
12+
private final JwtTokenProvider jwtTokenProvider;
13+
private final MemberRepository memberRepository;
14+
15+
public AuthService(JwtTokenProvider jwtTokenProvider, MemberRepository memberRepository) {
16+
this.jwtTokenProvider = jwtTokenProvider;
17+
this.memberRepository = memberRepository;
18+
}
19+
20+
public Member findLoginMemberByEmail(String email) {
21+
return memberRepository.findByEmail(email)
22+
.orElseThrow(() -> new AuthorizationException("사용자를 찾을 수 없습니다."));
23+
}
24+
25+
public String getEmailFromToken(String token) {
26+
return jwtTokenProvider.getPayload(token);
27+
}
28+
29+
public TokenResponse createToken(TokenRequest tokenRequest) {
30+
checkInvalidLogin(tokenRequest.getEmail(), tokenRequest.getPassword());
31+
32+
String accessToken = jwtTokenProvider.createToken(tokenRequest.getEmail());
33+
return new TokenResponse(accessToken);
34+
}
35+
36+
public void checkInvalidLogin(String email, String password) {
37+
Member member = memberRepository.findByEmailAndPassword(email, password)
38+
.orElseThrow(() -> new AuthorizationException("이메일 또는 비밀번호가 잘못되었습니다."));
39+
}
40+
41+
public void verifyToken(String token) {
42+
jwtTokenProvider.validateToken(token);
43+
}
44+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package roomescape.authentication;
2+
3+
import io.jsonwebtoken.*;
4+
import org.springframework.beans.factory.annotation.Value;
5+
import org.springframework.stereotype.Component;
6+
7+
import java.util.Date;
8+
9+
@Component
10+
public class JwtTokenProvider {
11+
@Value("${roomescape.auth.jwt.secret}")
12+
private String secretKey;
13+
14+
@Value("${roomescape.auth.jwt.expire-length}")
15+
private long validityInMilliseconds;
16+
17+
public String createToken(String payload) {
18+
Claims claims = Jwts.claims().setSubject(payload);
19+
Date now = new Date();
20+
Date validity = new Date(now.getTime() + validityInMilliseconds);
21+
return Jwts.builder()
22+
.setClaims(claims)
23+
.setIssuedAt(now)
24+
.setExpiration(validity)
25+
.signWith(SignatureAlgorithm.HS256, secretKey)
26+
.compact();
27+
}
28+
29+
public String getPayload(String token) {
30+
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
31+
}
32+
33+
public void validateToken(String token) {
34+
try {
35+
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
36+
if (claims.getBody().getExpiration().before(new Date())) {
37+
throw new IllegalArgumentException("토큰이 만료되었습니다.");
38+
}
39+
} catch (JwtException | IllegalArgumentException e) {
40+
throw new IllegalArgumentException("유효하지 않은 토큰 입니다.", e);
41+
}
42+
}
43+
44+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package roomescape.authentication;
2+
3+
import jakarta.servlet.http.Cookie;
4+
import jakarta.servlet.http.HttpServletRequest;
5+
import org.springframework.core.MethodParameter;
6+
import org.springframework.stereotype.Component;
7+
import org.springframework.web.bind.support.WebDataBinderFactory;
8+
import org.springframework.web.context.request.NativeWebRequest;
9+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
10+
import org.springframework.web.method.support.ModelAndViewContainer;
11+
import roomescape.exception.AuthorizationException;
12+
import roomescape.domain.member.Member;
13+
14+
import java.util.Arrays;
15+
16+
@Component
17+
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
18+
private final AuthService authService;
19+
20+
public LoginMemberArgumentResolver(AuthService authService) {
21+
this.authService = authService;
22+
}
23+
24+
@Override
25+
public boolean supportsParameter(MethodParameter parameter) {
26+
return parameter.getParameterType().equals(Member.class);
27+
}
28+
29+
@Override
30+
public Object resolveArgument(MethodParameter parameter,
31+
ModelAndViewContainer mavContainer,
32+
NativeWebRequest webRequest,
33+
WebDataBinderFactory binderFactory) throws Exception {
34+
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
35+
36+
String token = extractTokenFromCookies(request.getCookies());
37+
38+
authService.verifyToken(token);
39+
40+
String email = authService.getEmailFromToken(token);
41+
42+
return authService.findLoginMemberByEmail(email);
43+
}
44+
45+
private String extractTokenFromCookies(Cookie[] cookies) {
46+
if (cookies == null) {
47+
throw new AuthorizationException("쿠키가 존재하지 않습니다.");
48+
}
49+
50+
return Arrays.stream(cookies)
51+
.filter(cookie -> "token".equals(cookie.getName()))
52+
.findFirst()
53+
.map(Cookie::getValue)
54+
.orElseThrow(() -> new AuthorizationException("토큰 쿠키가 없습니다."));
55+
}
56+
}

src/main/java/roomescape/controller/ReservationController.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<<<<<<<< HEAD:src/main/java/roomescape/controller/ReservationController.java
12
package roomescape.controller;
23

34
import org.springframework.http.ResponseEntity;
@@ -62,3 +63,5 @@ public ResponseEntity delete(@PathVariable Long id) {
6263
return ResponseEntity.noContent().build();
6364
}
6465
}
66+
========
67+
>>>>>>>> origin/JPA:src/main/java/roomescape/reservation/ReservationController.java

src/main/java/roomescape/controller/ThemeController.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1+
<<<<<<<< HEAD:src/main/java/roomescape/controller/ThemeController.java
2+
<<<<<<<< HEAD:src/main/java/roomescape/controller/ThemeController.java
13
package roomescape.controller;
4+
========
5+
package roomescape.domain.theme;
6+
>>>>>>>> origin/JPA:src/main/java/roomescape/domain/theme/ThemeController.java
7+
========
8+
package roomescape.domain.theme;
9+
>>>>>>>> upstream/0094-gengar:src/main/java/roomescape/domain/theme/ThemeController.java
210

311
import org.springframework.http.ResponseEntity;
412
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -19,17 +27,29 @@ public class ThemeController {
1927

2028
public ThemeController(ThemeRepository themeRepository) {
2129
this.themeRepository = themeRepository;
30+
<<<<<<<< HEAD:src/main/java/roomescape/controller/ThemeController.java
31+
========
32+
}
33+
34+
@PostMapping("/themes")
35+
public ResponseEntity<Theme> createTheme(@RequestBody Theme theme) {
36+
Theme newTheme = themeRepository.save(theme);
37+
return ResponseEntity.created(URI.create("/themes/" + newTheme.getId())).body(newTheme);
38+
>>>>>>>> upstream/0094-gengar:src/main/java/roomescape/domain/theme/ThemeController.java
2239
}
2340

2441
@GetMapping("/themes")
2542
public ResponseEntity<List<Theme>> list() {
2643
return ResponseEntity.ok(themeRepository.findAll());
44+
<<<<<<<< HEAD:src/main/java/roomescape/controller/ThemeController.java
2745
}
2846

2947
@PostMapping("/themes")
3048
public ResponseEntity<Theme> createTheme(@RequestBody Theme theme) {
3149
Theme newTheme = themeRepository.save(theme);
3250
return ResponseEntity.created(URI.create("/themes/" + newTheme.getId())).body(newTheme);
51+
========
52+
>>>>>>>> upstream/0094-gengar:src/main/java/roomescape/domain/theme/ThemeController.java
3353
}
3454

3555
@DeleteMapping("/themes/{id}")

src/main/java/roomescape/controller/TimeController.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1+
<<<<<<<< HEAD:src/main/java/roomescape/controller/TimeController.java
2+
<<<<<<<< HEAD:src/main/java/roomescape/controller/TimeController.java
13
package roomescape.controller;
4+
========
5+
package roomescape.domain.time;
6+
>>>>>>>> origin/JPA:src/main/java/roomescape/domain/time/TimeController.java
7+
========
8+
package roomescape.domain.time;
9+
>>>>>>>> upstream/0094-gengar:src/main/java/roomescape/domain/time/TimeController.java
210

311
import org.springframework.http.ResponseEntity;
412
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -30,7 +38,7 @@ public List<Time> list() {
3038

3139
@PostMapping("/times")
3240
public ResponseEntity<Time> create(@RequestBody Time time) {
33-
if (time.getValue() == null || time.getValue().isEmpty()) {
41+
if (time.getTime() == null || time.getTime().isEmpty()) {
3442
throw new RuntimeException();
3543
}
3644

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package roomescape.controller;
2+
3+
import jakarta.servlet.http.Cookie;
4+
import jakarta.servlet.http.HttpServletResponse;
5+
import org.springframework.http.ResponseEntity;
6+
import org.springframework.web.bind.annotation.GetMapping;
7+
import org.springframework.web.bind.annotation.PostMapping;
8+
import org.springframework.web.bind.annotation.RequestBody;
9+
import org.springframework.web.bind.annotation.RestController;
10+
import roomescape.authentication.AuthService;
11+
import roomescape.domain.token.TokenRequest;
12+
import roomescape.domain.token.TokenResponse;
13+
import roomescape.domain.member.Member;
14+
import roomescape.domain.member.MemberResponse;
15+
16+
@RestController
17+
public class TokenLoginController {
18+
private final AuthService authService;
19+
20+
public TokenLoginController(AuthService authService) {
21+
this.authService = authService;
22+
}
23+
24+
@PostMapping("/login")
25+
public ResponseEntity<TokenResponse> tokenLogin(@RequestBody TokenRequest tokenRequest, HttpServletResponse response) {
26+
TokenResponse tokenResponse = authService.createToken(tokenRequest);
27+
28+
Cookie cookie = new Cookie("token", tokenResponse.getAccessToken());
29+
cookie.setHttpOnly(true);
30+
cookie.setPath("/");
31+
response.addCookie(cookie);
32+
33+
return ResponseEntity.ok().body(tokenResponse);
34+
}
35+
36+
@GetMapping("/login/check")
37+
public ResponseEntity<MemberResponse> checkLogin(Member loginMember) {
38+
MemberResponse memberResponse = new MemberResponse(
39+
loginMember.getId(),
40+
loginMember.getName(),
41+
loginMember.getEmail()
42+
);
43+
return ResponseEntity.ok(memberResponse);
44+
}
45+
}

src/main/java/roomescape/domain/member/LoginMember.java

Whitespace-only changes.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
<<<<<<<< HEAD:src/main/java/roomescape/domain/reservation/ReservationRequest.java
2+
<<<<<<<< HEAD:src/main/java/roomescape/domain/reservation/ReservationRequest.java
13
package roomescape.domain.reservation;
24

35
public class ReservationRequest {
@@ -35,3 +37,7 @@ public String getTime() {
3537
return time;
3638
}
3739
}
40+
========
41+
>>>>>>>> origin/JPA:src/main/java/roomescape/reservation/ReservationRequest.java
42+
========
43+
>>>>>>>> upstream/0094-gengar:src/main/java/roomescape/reservation/ReservationRequest.java
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package roomescape.domain.token;
2+
3+
public class TokenRequest {
4+
private String email;
5+
private String password;
6+
7+
public TokenRequest() {
8+
}
9+
10+
public TokenRequest(String email, String password) {
11+
this.email = email;
12+
this.password = password;
13+
}
14+
15+
public String getEmail() {
16+
return email;
17+
}
18+
19+
public String getPassword() {
20+
return password;
21+
}
22+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package roomescape.domain.token;
2+
3+
public class TokenResponse {
4+
private String accessToken;
5+
6+
public TokenResponse(String accessToken) {
7+
this.accessToken = accessToken;
8+
}
9+
10+
public String getAccessToken() {
11+
return accessToken;
12+
}
13+
}

src/main/java/roomescape/exception/AuthorizationException.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,21 @@
33
import org.springframework.http.HttpStatus;
44
import org.springframework.web.bind.annotation.ResponseStatus;
55

6-
@ResponseStatus(HttpStatus.UNAUTHORIZED)
6+
@ResponseStatus
77
public class AuthorizationException extends RuntimeException {
8+
<<<<<<<< HEAD:src/main/java/roomescape/exception/AuthorizationException.java
89
public AuthorizationException(String message) {
910
super(message);
11+
========
12+
private final HttpStatus httpStatus;
13+
14+
public AuthorizationException(String message, HttpStatus httpStatus) {
15+
super(message);
16+
this.httpStatus = httpStatus;
17+
}
18+
19+
public HttpStatus getHttpStatus() {
20+
return httpStatus;
21+
>>>>>>>> origin/JPA:src/main/java/roomescape/application/AuthorizationException.java
1022
}
1123
}

0 commit comments

Comments
 (0)