From e15d1811ef833189da402d6ec408e43a99bec375 Mon Sep 17 00:00:00 2001 From: westzeroright Date: Fri, 8 Nov 2024 22:54:59 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=EC=9D=91=EB=8B=B5=ED=97=A4=EB=8D=94?= =?UTF-8?q?=20=ED=86=A0=ED=81=B0=20=EC=BF=A0=ED=82=A4=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/application/service/AuthService.java | 2 +- .../auth/presentation/AuthController.java | 81 ++++++++++++++++--- .../member/presentation/MemberController.java | 31 ++++++- 3 files changed, 96 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/ordertogether/team14_be/auth/application/service/AuthService.java b/src/main/java/com/ordertogether/team14_be/auth/application/service/AuthService.java index 0bca91c1..33b62570 100644 --- a/src/main/java/com/ordertogether/team14_be/auth/application/service/AuthService.java +++ b/src/main/java/com/ordertogether/team14_be/auth/application/service/AuthService.java @@ -1,6 +1,6 @@ package com.ordertogether.team14_be.auth.application.service; -import com.ordertogether.team14_be.auth.JwtUtil; +import com.ordertogether.team14_be.auth.persistence.JwtUtil; import com.ordertogether.team14_be.member.application.service.MemberService; import com.ordertogether.team14_be.member.persistence.entity.Member; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/ordertogether/team14_be/auth/presentation/AuthController.java b/src/main/java/com/ordertogether/team14_be/auth/presentation/AuthController.java index 06b4824b..8c8bb3fa 100644 --- a/src/main/java/com/ordertogether/team14_be/auth/presentation/AuthController.java +++ b/src/main/java/com/ordertogether/team14_be/auth/presentation/AuthController.java @@ -6,22 +6,23 @@ import com.ordertogether.team14_be.member.application.dto.MemberInfoRequest; import com.ordertogether.team14_be.member.application.service.MemberService; import com.ordertogether.team14_be.member.persistence.entity.Member; -import java.net.URI; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; import java.util.Optional; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; 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 org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -@RestController +@Controller @RequestMapping("/api/v1/auth") public class AuthController { @@ -42,18 +43,39 @@ public AuthController( } @GetMapping("/login") - public ResponseEntity> getToken(@RequestHeader String authorizationCode) { + public ResponseEntity> getToken( + @RequestHeader("Authorization") String authorizationHeader, + HttpServletResponse httpServletResponse) { + String authorizationCode = authorizationHeader.replace("Bearer ", ""); + System.out.println("인가코드:" + authorizationCode); String userKakaoEmail = kakaoAuthService.getKakaoUserEmail(authorizationCode); + System.out.println("이메일:" + userKakaoEmail); Optional existMember = memberService.findMemberByEmail(userKakaoEmail); if (existMember.isPresent()) { - return ResponseEntity.ok( - ApiResponse.with(HttpStatus.OK, "로그인 성공", authService.getServiceToken(userKakaoEmail))); + String serviceToken = authService.getServiceToken(userKakaoEmail); + ResponseCookie cookie = + ResponseCookie.from("serviceToken", serviceToken) + .httpOnly(true) + .secure(true) + .path("/") + .sameSite("Strict") + .build(); + + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.SET_COOKIE, cookie.toString()); + + return ResponseEntity.ok() + .headers(headers) + .body(ApiResponse.with(HttpStatus.OK, "로그인 성공", serviceToken)); } else { - return ResponseEntity.status(HttpStatus.FOUND) - .location( - URI.create(redirectPage + URLEncoder.encode(userKakaoEmail, StandardCharsets.UTF_8))) - .build(); + String redirectUrl = redirectPage + userKakaoEmail; + try { + httpServletResponse.sendRedirect(redirectUrl); + } catch (IOException e) { + System.out.println(e.getMessage()); + } + return ResponseEntity.ok().body(ApiResponse.with(HttpStatus.OK, "리다이렉트", redirectUrl)); } } @@ -63,6 +85,39 @@ public ResponseEntity> signUpMember( String serviceToken = authService.register( email, memberInfoRequest.deliveryName(), memberInfoRequest.phoneNumber()); - return ResponseEntity.ok(ApiResponse.with(HttpStatus.OK, "로그인 성공", serviceToken)); + + ResponseCookie cookie = + ResponseCookie.from("serviceToken", serviceToken) + .httpOnly(true) + .secure(true) + .path("/") + .sameSite("Strict") + .build(); + + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.SET_COOKIE, cookie.toString()); + + return ResponseEntity.ok() + .headers(headers) + .body(ApiResponse.with(HttpStatus.OK, "회원가입 성공", serviceToken)); + } + + @PostMapping("/logout") + public ResponseEntity> logout(HttpServletResponse response) { + ResponseCookie deleteCookie = + ResponseCookie.from("serviceToken", "") + .maxAge(0) + .httpOnly(true) + .secure(true) + .path("/") + .sameSite("Strict") + .build(); + + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.SET_COOKIE, deleteCookie.toString()); + + return ResponseEntity.ok() + .headers(headers) + .body(ApiResponse.with(HttpStatus.OK, "회원가입 성공", "")); } } diff --git a/src/main/java/com/ordertogether/team14_be/member/presentation/MemberController.java b/src/main/java/com/ordertogether/team14_be/member/presentation/MemberController.java index 6251e840..4650ee01 100644 --- a/src/main/java/com/ordertogether/team14_be/member/presentation/MemberController.java +++ b/src/main/java/com/ordertogether/team14_be/member/presentation/MemberController.java @@ -1,17 +1,22 @@ package com.ordertogether.team14_be.member.presentation; +import com.ordertogether.team14_be.auth.persistence.JwtUtil; import com.ordertogether.team14_be.common.web.response.ApiResponse; import com.ordertogether.team14_be.member.application.dto.MemberInfoRequest; import com.ordertogether.team14_be.member.application.dto.MemberInfoResponse; import com.ordertogether.team14_be.member.application.service.MemberService; import com.ordertogether.team14_be.member.persistence.entity.Member; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; 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.PutMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -19,8 +24,8 @@ @RestController @RequestMapping("api/v1/members") public class MemberController { - private final MemberService memberService; + private final JwtUtil jwtUtil; @GetMapping public ResponseEntity> getMemberInfo(@LoginMember Member member) { @@ -38,8 +43,26 @@ public ResponseEntity> modifyMemberInfo( } @DeleteMapping - public ResponseEntity deleteMember(@LoginMember Member member) { - memberService.deleteMember(member.getId()); - return ResponseEntity.ok(ApiResponse.with(HttpStatus.OK, "회원 정보가 삭제되었습니다.", "")); + public ResponseEntity deleteMember( + @RequestHeader("Authorization") String authorizationHeader, + HttpServletResponse httpServletResponse) { + ResponseCookie deleteCookie = + ResponseCookie.from("serviceToken", "") + .maxAge(0) + .httpOnly(true) + .secure(true) + .path("/") + .sameSite("Strict") + .build(); + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.SET_COOKIE, deleteCookie.toString()); + + String serviceToken = authorizationHeader.replace("Bearer ", ""); + String stringMemberId = jwtUtil.decodeJwt(serviceToken).getSubject(); + long memberId = Integer.parseInt(stringMemberId); + memberService.deleteMember(memberId); + return ResponseEntity.ok() + .headers(headers) + .body(ApiResponse.with(HttpStatus.OK, "회원탈퇴 성공", "")); } } From 5a37c04161491f23481e8bab40f7c90c8630b72b Mon Sep 17 00:00:00 2001 From: westzeroright Date: Fri, 8 Nov 2024 22:55:23 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20JwtInterceptor=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/persistence/JwtInterceptor.java | 48 +++++++++++++++++++ .../auth/{ => persistence}/JwtUtil.java | 17 ++++++- .../persistence/exception/InvalidToken.java | 7 +++ .../team14_be/config/WebConfig.java | 19 ++++++-- .../LoginMemberArgumentResolver.java | 2 +- .../application/exception/NotFoundMember.java | 3 +- 6 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/ordertogether/team14_be/auth/persistence/JwtInterceptor.java rename src/main/java/com/ordertogether/team14_be/auth/{ => persistence}/JwtUtil.java (69%) create mode 100644 src/main/java/com/ordertogether/team14_be/auth/persistence/exception/InvalidToken.java diff --git a/src/main/java/com/ordertogether/team14_be/auth/persistence/JwtInterceptor.java b/src/main/java/com/ordertogether/team14_be/auth/persistence/JwtInterceptor.java new file mode 100644 index 00000000..e6ce3ba1 --- /dev/null +++ b/src/main/java/com/ordertogether/team14_be/auth/persistence/JwtInterceptor.java @@ -0,0 +1,48 @@ +package com.ordertogether.team14_be.auth.persistence; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ordertogether.team14_be.member.application.exception.NotFoundMember; +import com.ordertogether.team14_be.member.persistence.entity.Member; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +@RequiredArgsConstructor +@Slf4j +@Component +public class JwtInterceptor implements HandlerInterceptor { + private final JwtUtil jwtUtil; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) + throws Exception { + if (HttpMethod.OPTIONS.matches(request.getMethod())) { + return true; + } + + String authorization = request.getHeader(HttpHeaders.AUTHORIZATION); + String token = authorization.replaceAll("Bearer ", ""); + + if (token != null && token.length() > 10) { + log.debug("토큰 상태:: " + token); + + if (jwtUtil.vaildToken(token)) { + ObjectMapper objectMapper = new ObjectMapper(); + + String member = objectMapper.writeValueAsString(jwtUtil.decodeJwt(token).get("member")); + Member accessMember = objectMapper.readValue(member, Member.class); + + request.setAttribute("member", accessMember); + return true; + } + } else { + throw new NotFoundMember(); + } + return false; + } +} diff --git a/src/main/java/com/ordertogether/team14_be/auth/JwtUtil.java b/src/main/java/com/ordertogether/team14_be/auth/persistence/JwtUtil.java similarity index 69% rename from src/main/java/com/ordertogether/team14_be/auth/JwtUtil.java rename to src/main/java/com/ordertogether/team14_be/auth/persistence/JwtUtil.java index deaeb9c7..d756f440 100644 --- a/src/main/java/com/ordertogether/team14_be/auth/JwtUtil.java +++ b/src/main/java/com/ordertogether/team14_be/auth/persistence/JwtUtil.java @@ -1,7 +1,9 @@ -package com.ordertogether.team14_be.auth; +package com.ordertogether.team14_be.auth.persistence; +import com.ordertogether.team14_be.auth.persistence.exception.InvalidToken; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import java.nio.charset.StandardCharsets; @@ -38,4 +40,17 @@ public String generateToken(Long data) { public Claims decodeJwt(String token) { return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody(); } + + public boolean vaildToken(String token) throws InvalidToken { + try { + Claims claims = + Jwts.parser() + .setSigningKey(key) + .parseClaimsJws(token) // 토큰 파싱 + .getBody(); + return true; // 유효하다면 true 반환 + } catch (MalformedJwtException e) { + throw new InvalidToken(); + } + } } diff --git a/src/main/java/com/ordertogether/team14_be/auth/persistence/exception/InvalidToken.java b/src/main/java/com/ordertogether/team14_be/auth/persistence/exception/InvalidToken.java new file mode 100644 index 00000000..82052799 --- /dev/null +++ b/src/main/java/com/ordertogether/team14_be/auth/persistence/exception/InvalidToken.java @@ -0,0 +1,7 @@ +package com.ordertogether.team14_be.auth.persistence.exception; + +public class InvalidToken extends IllegalAccessException { + public InvalidToken() { + super("토큰이 유효하지 않습니다."); + } +} diff --git a/src/main/java/com/ordertogether/team14_be/config/WebConfig.java b/src/main/java/com/ordertogether/team14_be/config/WebConfig.java index 9b911586..40c2aa71 100644 --- a/src/main/java/com/ordertogether/team14_be/config/WebConfig.java +++ b/src/main/java/com/ordertogether/team14_be/config/WebConfig.java @@ -1,21 +1,32 @@ package com.ordertogether.team14_be.config; +import com.ordertogether.team14_be.auth.persistence.JwtInterceptor; import com.ordertogether.team14_be.member.application.LoginMemberArgumentResolver; import java.util.List; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration +@RequiredArgsConstructor +@EnableWebMvc public class WebConfig implements WebMvcConfigurer { private final LoginMemberArgumentResolver loginMemberArgumentResolver; - - public WebConfig(LoginMemberArgumentResolver loginMemberArgumentResolver) { - this.loginMemberArgumentResolver = loginMemberArgumentResolver; - } + private final JwtInterceptor jwtInterceptor; @Override public void addArgumentResolvers(List argumentResolvers) { argumentResolvers.add(loginMemberArgumentResolver); } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry + .addInterceptor(jwtInterceptor) + .addPathPatterns("/**") + .excludePathPatterns("/signup", "/api/v1/auth/signup", "/api/v1/auth/login"); + } } diff --git a/src/main/java/com/ordertogether/team14_be/member/application/LoginMemberArgumentResolver.java b/src/main/java/com/ordertogether/team14_be/member/application/LoginMemberArgumentResolver.java index 543bd5a6..aeabaf7a 100644 --- a/src/main/java/com/ordertogether/team14_be/member/application/LoginMemberArgumentResolver.java +++ b/src/main/java/com/ordertogether/team14_be/member/application/LoginMemberArgumentResolver.java @@ -1,6 +1,6 @@ package com.ordertogether.team14_be.member.application; -import com.ordertogether.team14_be.auth.JwtUtil; +import com.ordertogether.team14_be.auth.persistence.JwtUtil; import com.ordertogether.team14_be.member.application.service.MemberService; import com.ordertogether.team14_be.member.persistence.MemberRepository; import com.ordertogether.team14_be.member.presentation.LoginMember; diff --git a/src/main/java/com/ordertogether/team14_be/member/application/exception/NotFoundMember.java b/src/main/java/com/ordertogether/team14_be/member/application/exception/NotFoundMember.java index 23591c70..c7beeff2 100644 --- a/src/main/java/com/ordertogether/team14_be/member/application/exception/NotFoundMember.java +++ b/src/main/java/com/ordertogether/team14_be/member/application/exception/NotFoundMember.java @@ -3,8 +3,7 @@ import java.util.NoSuchElementException; public class NotFoundMember extends NoSuchElementException { - public NotFoundMember() { - super("회원정보가 존재하지 않습니다"); + super("회원정보가 존재하지 않습니다."); } } From 615e9d535e8f7323fac9bb66a1a07531ac9c8021 Mon Sep 17 00:00:00 2001 From: westzeroright Date: Tue, 12 Nov 2024 23:23:02 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=EC=8A=A4=ED=8C=9F=20API=EB=8F=84?= =?UTF-8?q?=20=EC=9D=B8=ED=84=B0=EC=85=89=ED=84=B0=20=EC=A0=9C=EC=99=B8=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../team14_be/auth/presentation/AuthController.java | 4 +--- .../java/com/ordertogether/team14_be/config/WebConfig.java | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/ordertogether/team14_be/auth/presentation/AuthController.java b/src/main/java/com/ordertogether/team14_be/auth/presentation/AuthController.java index 8c8bb3fa..6bd4fa93 100644 --- a/src/main/java/com/ordertogether/team14_be/auth/presentation/AuthController.java +++ b/src/main/java/com/ordertogether/team14_be/auth/presentation/AuthController.java @@ -47,9 +47,7 @@ public ResponseEntity> getToken( @RequestHeader("Authorization") String authorizationHeader, HttpServletResponse httpServletResponse) { String authorizationCode = authorizationHeader.replace("Bearer ", ""); - System.out.println("인가코드:" + authorizationCode); String userKakaoEmail = kakaoAuthService.getKakaoUserEmail(authorizationCode); - System.out.println("이메일:" + userKakaoEmail); Optional existMember = memberService.findMemberByEmail(userKakaoEmail); if (existMember.isPresent()) { String serviceToken = authService.getServiceToken(userKakaoEmail); @@ -118,6 +116,6 @@ public ResponseEntity> logout(HttpServletResponse response) return ResponseEntity.ok() .headers(headers) - .body(ApiResponse.with(HttpStatus.OK, "회원가입 성공", "")); + .body(ApiResponse.with(HttpStatus.OK, "로그아웃 성공", "")); } } diff --git a/src/main/java/com/ordertogether/team14_be/config/WebConfig.java b/src/main/java/com/ordertogether/team14_be/config/WebConfig.java index 40c2aa71..6c8dc57a 100644 --- a/src/main/java/com/ordertogether/team14_be/config/WebConfig.java +++ b/src/main/java/com/ordertogether/team14_be/config/WebConfig.java @@ -27,6 +27,7 @@ public void addInterceptors(InterceptorRegistry registry) { registry .addInterceptor(jwtInterceptor) .addPathPatterns("/**") - .excludePathPatterns("/signup", "/api/v1/auth/signup", "/api/v1/auth/login"); + .excludePathPatterns( + "/signup", "/api/v1/auth/signup", "/api/v1/auth/login", "/api/v1/spot/**"); } }