From 30bcef477a28f20b9bf8dbf1600766093b98be49 Mon Sep 17 00:00:00 2001 From: sksmsdlskgus Date: Fri, 29 Nov 2024 10:01:42 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=E2=9C=A8=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20:RedisService=20logout=EC=8B=9C=20=EC=BF=A0?= =?UTF-8?q?=ED=82=A4=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20redis=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/controller/AdminController.java | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/LearnsMate/src/main/java/intbyte4/learnsmate/admin/controller/AdminController.java b/LearnsMate/src/main/java/intbyte4/learnsmate/admin/controller/AdminController.java index 31696163..b7014830 100644 --- a/LearnsMate/src/main/java/intbyte4/learnsmate/admin/controller/AdminController.java +++ b/LearnsMate/src/main/java/intbyte4/learnsmate/admin/controller/AdminController.java @@ -13,10 +13,12 @@ import intbyte4.learnsmate.admin.mapper.AdminMapper; import intbyte4.learnsmate.admin.service.AdminService; import intbyte4.learnsmate.admin.service.EmailService; +import intbyte4.learnsmate.admin.service.RedisService; import intbyte4.learnsmate.common.exception.CommonException; import intbyte4.learnsmate.common.exception.StatusEnum; import io.swagger.v3.oas.annotations.Operation; import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -38,6 +40,7 @@ public class AdminController { private final AdminService adminService; private final AdminMapper adminMapper; private final EmailService emailService; + private final RedisService redisService; @Operation(summary = "직원 단건 조회") @GetMapping("/{adminCode}") @@ -74,16 +77,41 @@ public ResponseEntity> checkAuthStatus(@AuthenticationPrinci @Operation(summary = "직원 로그아웃") @PostMapping("/logout") - public ResponseEntity logout(HttpServletResponse response) { + public ResponseEntity logout(HttpServletRequest request,HttpServletResponse response) { log.info("POST /admin/logout 요청 도착"); - // 쿠키 삭제 명령 - Cookie cookie = new Cookie("token", null); - cookie.setPath("/"); - cookie.setHttpOnly(true); - cookie.setMaxAge(0); // 쿠키 만료 처리 - response.addCookie(cookie); - - // 필요 시 블랙리스트로 JWT 관리 + + // 쿠키에서 refreshToken 추출 + String refreshToken = null; + + // 쿠키에서 refreshToken 추출 + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) { + if ("refreshToken".equals(cookie.getName())) { + refreshToken = cookie.getValue(); + } + } + } + + // 쿠키 삭제 + Cookie accessTokenCookie = new Cookie("accessToken", null); + accessTokenCookie.setPath("/"); + accessTokenCookie.setHttpOnly(true); + accessTokenCookie.setMaxAge(0); // 쿠키 만료 처리 (브라우저에서 삭제됨) + response.addCookie(accessTokenCookie); + + Cookie refreshTokenCookie = new Cookie("refreshToken", null); + refreshTokenCookie.setPath("/"); + refreshTokenCookie.setHttpOnly(true); + refreshTokenCookie.setMaxAge(0); // 쿠키 만료 처리 (브라우저에서 삭제됨) + response.addCookie(refreshTokenCookie); + + // Redis에서 refreshToken 삭제 + if (refreshToken != null && !refreshToken.isEmpty()) { + redisService.deleteToken(refreshToken); // Redis에서 refreshToken 삭제 + } + + log.info("로그아웃 성공"); return ResponseEntity.ok().body("로그아웃 성공"); } From 1f8082122e3217f30b07392008179740b7dcd623 Mon Sep 17 00:00:00 2001 From: sksmsdlskgus Date: Fri, 29 Nov 2024 10:02:18 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=E2=9C=A8=20=EA=B8=B0=EB=8A=A5=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20:Cookie=20=EA=B8=B0=EB=8A=A5=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../learnsmate/security/AuthenticationFilter.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/LearnsMate/src/main/java/intbyte4/learnsmate/security/AuthenticationFilter.java b/LearnsMate/src/main/java/intbyte4/learnsmate/security/AuthenticationFilter.java index 294bee7a..f82e7c1a 100644 --- a/LearnsMate/src/main/java/intbyte4/learnsmate/security/AuthenticationFilter.java +++ b/LearnsMate/src/main/java/intbyte4/learnsmate/security/AuthenticationFilter.java @@ -83,6 +83,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR throw new IllegalArgumentException("Authentication 객체가 CustomUserDetails 타입이 아닙니다."); } + // CustomUserDetails로 캐스팅하여 사용자 정보를 가져옴 CustomUserDetails userDetails = (CustomUserDetails) authResult.getPrincipal(); // 사용자 정보 가져오기 @@ -100,11 +101,11 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR // JWT 생성: JwtTokenDTO에 사용자 정보를 담고, roles와 함께 JWT 토큰을 생성 JwtTokenDTO tokenDTO = new JwtTokenDTO(userCode, userEmail, userName); - String accessToken = jwtUtil.generateToken(tokenDTO, roles, null); // JWT 생성 (roles와 추가적인 데이터를 페이로드에 담음) + String token = jwtUtil.generateToken(tokenDTO, roles, null); // JWT 생성 (roles와 추가적인 데이터를 페이로드에 담음) String refreshToken = jwtUtil.generateRefreshToken(tokenDTO); // 7일 // 쿠키 생성 - Cookie jwtCookie = new Cookie("accessToken", accessToken); + Cookie jwtCookie = new Cookie("token", token); jwtCookie.setHttpOnly(true); // HTTP Only 속성으로 설정 (JavaScript에서 접근 불가) jwtCookie.setSecure(false); // HTTPS 연결에서만 전송 (테스트 환경에서는 false 설정 가능) // https://learnsmate.site -> 배포 환경시 true로 전환 @@ -112,6 +113,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR jwtCookie.setMaxAge(4 * 3600); // 쿠키 만료 시간 설정 (4시간) // 여기를 3-4시간정도로 만료시간 할건데 리프레시토큰을 해야하나? erp라 재로그인이 필요하지않을까 + response.addCookie(jwtCookie); // Refresh Token 쿠키 생성 Cookie refreshTokenCookie = new Cookie("refreshToken", refreshToken); @@ -120,8 +122,6 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR refreshTokenCookie.setPath("/"); // 유효 경로 설정 refreshTokenCookie.setMaxAge(7 * 24 * 3600); // Refresh Token의 만료 시간 (7일) - // 쿠키 응답에 추가 - response.addCookie(jwtCookie); response.addCookie(refreshTokenCookie); saveRefreshTokenToRedis(userCode,refreshToken); From 0261426a99b6e07876ac2d575c6a5e4448401205 Mon Sep 17 00:00:00 2001 From: sksmsdlskgus Date: Fri, 29 Nov 2024 10:02:40 +0900 Subject: [PATCH 3/5] =?UTF-8?q?=E2=9C=A8=20=EA=B8=B0=EB=8A=A5=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20:JWT=20=ED=86=A0=ED=81=B0=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=EC=9D=84=20=EC=A0=9C=EC=99=B8=ED=95=A0=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/intbyte4/learnsmate/security/JwtFilter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/LearnsMate/src/main/java/intbyte4/learnsmate/security/JwtFilter.java b/LearnsMate/src/main/java/intbyte4/learnsmate/security/JwtFilter.java index 5fb90658..bcf5f802 100644 --- a/LearnsMate/src/main/java/intbyte4/learnsmate/security/JwtFilter.java +++ b/LearnsMate/src/main/java/intbyte4/learnsmate/security/JwtFilter.java @@ -23,9 +23,9 @@ public class JwtFilter extends OncePerRequestFilter { private final AdminService userService; private final JwtUtil jwtUtil; // JWT 토큰을 다루는 JwtUtil 클래스 private final List excludeUrl // JWT 토큰 검증을 제외할 URL 패턴 목록 - = Arrays.asList("/users/verification-email/**" - , "/users/nickname/check", "/users/verification-email/password", "/swagger-ui.html", "/swagger-ui/index.html" - , "/users/password"); + = Arrays.asList("/admin/verification-email/**" + , "/admin/verification-email/password", "/swagger-ui.html", "/swagger-ui/index.html" + , "/admin/password"); public JwtFilter(AdminService userService, JwtUtil jwtUtil) { this.userService = userService; From bb85c0e3f89e619139afc838642e5f6c9ee013c8 Mon Sep 17 00:00:00 2001 From: sksmsdlskgus Date: Fri, 29 Nov 2024 10:03:10 +0900 Subject: [PATCH 4/5] =?UTF-8?q?=E2=9C=A8=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20:/auth/=20=EA=B2=BD=EB=A1=9C=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/intbyte4/learnsmate/security/WebSecurity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/LearnsMate/src/main/java/intbyte4/learnsmate/security/WebSecurity.java b/LearnsMate/src/main/java/intbyte4/learnsmate/security/WebSecurity.java index 6d6eb5f5..6a0ae876 100644 --- a/LearnsMate/src/main/java/intbyte4/learnsmate/security/WebSecurity.java +++ b/LearnsMate/src/main/java/intbyte4/learnsmate/security/WebSecurity.java @@ -67,6 +67,8 @@ protected SecurityFilterChain configure(HttpSecurity http) throws Exception { .requestMatchers(new AntPathRequestMatcher("/swagger-ui/**", "GET")).permitAll() .requestMatchers(new AntPathRequestMatcher("/v3/api-docs/**", "GET")).permitAll() .requestMatchers(new AntPathRequestMatcher("/admin/**","GET")).permitAll() + .requestMatchers(new AntPathRequestMatcher("/auth/**","GET")).permitAll() + .requestMatchers(new AntPathRequestMatcher("/auth/**","POST")).permitAll() .requestMatchers(new AntPathRequestMatcher("/admin/**","POST")).permitAll() .requestMatchers(new AntPathRequestMatcher("/users/**", "POST")).permitAll() .requestMatchers(new AntPathRequestMatcher("/users/**", "OPTIONS")).permitAll() From 3aefc8ec4b5f206c879181b8367436c8f91aa4a3 Mon Sep 17 00:00:00 2001 From: sksmsdlskgus Date: Fri, 29 Nov 2024 10:03:23 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=E2=9C=A8=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20:RedisService=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/service/RedisService.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 LearnsMate/src/main/java/intbyte4/learnsmate/admin/service/RedisService.java diff --git a/LearnsMate/src/main/java/intbyte4/learnsmate/admin/service/RedisService.java b/LearnsMate/src/main/java/intbyte4/learnsmate/admin/service/RedisService.java new file mode 100644 index 00000000..e41c3ad1 --- /dev/null +++ b/LearnsMate/src/main/java/intbyte4/learnsmate/admin/service/RedisService.java @@ -0,0 +1,22 @@ +package intbyte4.learnsmate.admin.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +@Service +public class RedisService { + + private final RedisTemplate redisTemplate; + + @Autowired + public RedisService(RedisTemplate redisTemplate) { + this.redisTemplate = redisTemplate; + } + + public void deleteToken(String refreshToken) { + if (refreshToken != null && !refreshToken.isEmpty()) { + redisTemplate.delete("refreshToken:" + refreshToken); // Redis에서 삭제 + } + } +}