Skip to content

Commit 76b5f64

Browse files
committed
conflict fix 3
2 parents abdef60 + 1b87547 commit 76b5f64

22 files changed

+695
-1
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package roomescape.UI;
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.application.AuthService;
11+
import roomescape.dto.TokenRequest;
12+
import roomescape.dto.TokenResponse;
13+
import roomescape.member.LoginMember;
14+
import roomescape.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(LoginMember loginMember) {
38+
MemberResponse memberResponse = new MemberResponse(
39+
loginMember.getId(),
40+
loginMember.getName(),
41+
loginMember.getEmail()
42+
);
43+
return ResponseEntity.ok(memberResponse);
44+
}
45+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package roomescape.application;
2+
3+
import org.springframework.stereotype.Service;
4+
import roomescape.dto.TokenRequest;
5+
import roomescape.dto.TokenResponse;
6+
import roomescape.infrastructure.JwtTokenProvider;
7+
import roomescape.member.LoginMember;
8+
import roomescape.member.Member;
9+
import roomescape.member.MemberDao;
10+
11+
import static org.springframework.http.HttpStatus.*;
12+
13+
@Service
14+
public class AuthService {
15+
private final JwtTokenProvider jwtTokenProvider;
16+
private final MemberDao memberDao;
17+
18+
public AuthService(JwtTokenProvider jwtTokenProvider, MemberDao memberDao) {
19+
this.jwtTokenProvider = jwtTokenProvider;
20+
this.memberDao = memberDao;
21+
}
22+
23+
public LoginMember findLoginMemberByEmail(String email) {
24+
Member member = memberDao.findByEmail(email);
25+
if (member == null) {
26+
throw new AuthorizationException("사용자를 찾을 수 없습니다.", NOT_FOUND);
27+
}
28+
return new LoginMember(member.getId(), member.getName(), member.getEmail(), member.getRole());
29+
}
30+
31+
public String getEmailFromToken(String token) {
32+
return jwtTokenProvider.getPayload(token);
33+
}
34+
35+
public TokenResponse createToken(TokenRequest tokenRequest) {
36+
if (checkInvalidLogin(tokenRequest.getEmail(), tokenRequest.getPassword())) {
37+
throw new AuthorizationException("유효한 로그인 정보가 아닙니다.", BAD_REQUEST);
38+
}
39+
String accessToken = jwtTokenProvider.createToken(tokenRequest.getEmail());
40+
return new TokenResponse(accessToken);
41+
}
42+
43+
public boolean checkInvalidLogin(String email, String password) {
44+
Member member = memberDao.findByEmailAndPassword(email, password);
45+
if (member == null) {
46+
throw new AuthorizationException("이메일 또는 비밀번호가 잘못되었습니다.", UNAUTHORIZED);
47+
}
48+
return false;
49+
}
50+
51+
public boolean verifyToken(String token) {
52+
try {
53+
return jwtTokenProvider.validateToken(token);
54+
} catch (Exception e) {
55+
return false;
56+
}
57+
}
58+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package roomescape.application;
2+
3+
import org.springframework.http.HttpStatus;
4+
import org.springframework.web.bind.annotation.ResponseStatus;
5+
6+
@ResponseStatus
7+
public class AuthorizationException extends RuntimeException {
8+
private final HttpStatus httpStatus;
9+
10+
public AuthorizationException(String message, HttpStatus httpStatus) {
11+
super(message);
12+
this.httpStatus = httpStatus;
13+
}
14+
15+
public HttpStatus getHttpStatus() {
16+
return httpStatus;
17+
}
18+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package roomescape.application;
2+
3+
import org.springframework.http.ResponseEntity;
4+
import org.springframework.web.bind.annotation.ExceptionHandler;
5+
import org.springframework.web.bind.annotation.RestControllerAdvice;
6+
7+
@RestControllerAdvice
8+
public class GlobalExceptionHandler {
9+
10+
@ExceptionHandler(AuthorizationException.class)
11+
public ResponseEntity<String> handleAuthorizationException(AuthorizationException ex) {
12+
return ResponseEntity
13+
.status(ex.getHttpStatus())
14+
.body("Unauthorized: " + ex.getMessage());
15+
}
16+
}
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+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package roomescape.config;
2+
3+
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
5+
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
6+
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
7+
import roomescape.application.AuthService;
8+
import roomescape.security.AdminAccessInterceptor;
9+
import roomescape.web.LoginMemberArgumentResolver;
10+
11+
import java.util.List;
12+
13+
@Configuration
14+
public class WebConfig implements WebMvcConfigurer {
15+
private final AuthService authService;
16+
17+
public WebConfig(AuthService authService) {
18+
this.authService = authService;
19+
}
20+
21+
@Override
22+
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
23+
resolvers.add(new LoginMemberArgumentResolver(authService));
24+
}
25+
26+
@Override
27+
public void addInterceptors(InterceptorRegistry registry) {
28+
registry.addInterceptor(new AdminAccessInterceptor(authService))
29+
.addPathPatterns("/admin/**")
30+
.excludePathPatterns("/login");
31+
}
32+
}

0 commit comments

Comments
 (0)