From edf1c34a693893e0a233f4fbf89827f343e5fef2 Mon Sep 17 00:00:00 2001 From: chwangmin Date: Wed, 22 May 2024 23:06:05 +0900 Subject: [PATCH 1/4] =?UTF-8?q?refactor=20:=20jwt=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=97=90=EB=9F=AC=20filter=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=B2=98=EB=A6=AC=20=EB=B0=8F=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/filter/JwtAuthenticationFilter.java | 55 ++++++++++--------- .../auth/filter/JwtExceptionFilter.java | 47 ++++++++++++++++ .../security/WebSecurityConfiguration.java | 4 +- 3 files changed, 80 insertions(+), 26 deletions(-) create mode 100644 backend/src/main/java/com/ssafy/home/global/auth/filter/JwtExceptionFilter.java diff --git a/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtAuthenticationFilter.java b/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtAuthenticationFilter.java index bf8c445..29d986e 100644 --- a/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtAuthenticationFilter.java +++ b/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtAuthenticationFilter.java @@ -4,6 +4,8 @@ import com.ssafy.home.entity.member.Member; import com.ssafy.home.global.auth.dto.MemberDto; import com.ssafy.home.global.auth.jwt.JwtTokenProvider; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.JwtException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.Cookie; @@ -34,43 +36,28 @@ public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider, MemberService @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String authHeader = request.getHeader("Authorization"); + + String token = ""; + if(authHeader == null || !authHeader.startsWith("Bearer ")) { - log.error("header에 없거나, 형식이 틀립니다. - {}", authHeader); - filterChain.doFilter(request,response); - return; + refreshTokenCheck(request, response, token); + throw new JwtException("header에 없거나, 형식이 틀립니다."); } - String token; try{ token = authHeader.split(" ")[1].trim(); } catch (Exception e) { - log.error("토큰을 분리하는데 실패했습니다. - {}", authHeader); - filterChain.doFilter(request,response); - return; + refreshTokenCheck(request, response, token); + throw new JwtException("액세스 토큰을 분리하는데 실패"); } - log.info("token : {}", token); if(!jwtTokenProvider.validateToken(token)){ - - Cookie[] cookies = request.getCookies(); - - String refreshToken = null; - - if (cookies != null) { - refreshToken = Arrays.stream(cookies) - .filter(cookie -> cookie.getName().equals("refreshToken")) - .map(Cookie::getValue) - .findFirst() - .orElse(null); - } - - token = memberService.reissue(refreshToken); - - response.addHeader(JwtTokenProvider.AUTHORIZATION_HEADER, token); + refreshTokenCheck(request, response, token); + throw new ExpiredJwtException(null, null, "액세스 토큰 만료"); } Long userId = jwtTokenProvider.getInfoId(token); - log.info("userId : {}", userId); + Member member = memberService.getMemberById(userId); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( @@ -87,4 +74,22 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse filterChain.doFilter(request, response); } + private void refreshTokenCheck(HttpServletRequest request, HttpServletResponse response, String token){ + Cookie[] cookies = request.getCookies(); + + String refreshToken = null; + + if (cookies != null) { + refreshToken = Arrays.stream(cookies) + .filter(cookie -> cookie.getName().equals("refreshToken")) + .map(Cookie::getValue) + .findFirst() + .orElse(null); + } + + token = memberService.reissue(refreshToken); + + response.addHeader(JwtTokenProvider.AUTHORIZATION_HEADER, token); + } + } diff --git a/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtExceptionFilter.java b/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtExceptionFilter.java new file mode 100644 index 0000000..601b551 --- /dev/null +++ b/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtExceptionFilter.java @@ -0,0 +1,47 @@ +package com.ssafy.home.global.auth.filter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ssafy.home.global.error.ErrorCode; +import com.ssafy.home.global.error.ErrorResponse; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.JwtException; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +@Slf4j +public class JwtExceptionFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + try { + filterChain.doFilter(request, response); + } catch (ExpiredJwtException e) { + setErrorResponse(response, ErrorCode.TOKEN_EXPIRED); + } catch (JwtException | IllegalArgumentException | NullPointerException | UnsupportedEncodingException e) { + setErrorResponse(response, ErrorCode.NOT_VALID_TOKEN); + } + } + + private void setErrorResponse(HttpServletResponse response, ErrorCode errorCode) { + log.error("filter에서 에러 체크"); + + ObjectMapper objectMapper = new ObjectMapper(); + response.setStatus(errorCode.getHttpStatus().value()); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding("UTF-8"); + + try { + response.getWriter().write(objectMapper.writeValueAsString(ErrorResponse.of(errorCode.getErrorCode(),errorCode.getMessage()))); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/com/ssafy/home/global/config/security/WebSecurityConfiguration.java b/backend/src/main/java/com/ssafy/home/global/config/security/WebSecurityConfiguration.java index 01d8140..52ea6db 100644 --- a/backend/src/main/java/com/ssafy/home/global/config/security/WebSecurityConfiguration.java +++ b/backend/src/main/java/com/ssafy/home/global/config/security/WebSecurityConfiguration.java @@ -2,6 +2,7 @@ import com.ssafy.home.domain.member.service.MemberService; import com.ssafy.home.global.auth.filter.JwtAuthenticationFilter; +import com.ssafy.home.global.auth.filter.JwtExceptionFilter; import com.ssafy.home.global.auth.interceptor.UserActivationInterceptor; import com.ssafy.home.global.auth.jwt.JwtTokenProvider; import lombok.RequiredArgsConstructor; @@ -38,7 +39,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .httpBasic(AbstractHttpConfigurer::disable) .formLogin(AbstractHttpConfigurer::disable) .logout(AbstractHttpConfigurer::disable) - .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider, memberService), BasicAuthenticationFilter.class); + .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider, memberService), BasicAuthenticationFilter.class) + .addFilterBefore(new JwtExceptionFilter(), JwtAuthenticationFilter.class); return http.build(); } From 2a6bdf0a8022894efc62d22ae4c197f083b0f6a1 Mon Sep 17 00:00:00 2001 From: chwangmin Date: Wed, 22 May 2024 23:25:09 +0900 Subject: [PATCH 2/4] =?UTF-8?q?refactor=20:=20accessToken=20=EB=A7=8C?= =?UTF-8?q?=EB=A3=8C,=20refreshToken=20=EA=B2=80=EC=A6=9D=20=EC=8B=9C,=20a?= =?UTF-8?q?ccessToken=20=EC=9E=AC=EB=B0=9C=EA=B8=89=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=20reponse=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/domain/member/service/MemberService.java | 4 ++-- .../global/auth/filter/JwtAuthenticationFilter.java | 11 +++++++---- .../home/global/auth/filter/JwtExceptionFilter.java | 2 +- .../java/com/ssafy/home/global/error/ErrorCode.java | 1 + 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/backend/src/main/java/com/ssafy/home/domain/member/service/MemberService.java b/backend/src/main/java/com/ssafy/home/domain/member/service/MemberService.java index 8f6c2ef..680f619 100644 --- a/backend/src/main/java/com/ssafy/home/domain/member/service/MemberService.java +++ b/backend/src/main/java/com/ssafy/home/domain/member/service/MemberService.java @@ -122,9 +122,9 @@ public String reissue(String refreshToken) { .orElseThrow(() -> new AuthenticationException(ErrorCode.REFRESH_TOKEN_NOT_FOUND)); return jwtTokenProvider.createAccessToken(member); + } else { + throw new AuthenticationException(ErrorCode.NOT_VALID_TOKEN); } - - return null; } public Member getMemberById(Long memberId) { diff --git a/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtAuthenticationFilter.java b/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtAuthenticationFilter.java index 29d986e..ed40a08 100644 --- a/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtAuthenticationFilter.java +++ b/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtAuthenticationFilter.java @@ -41,14 +41,14 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse if(authHeader == null || !authHeader.startsWith("Bearer ")) { refreshTokenCheck(request, response, token); - throw new JwtException("header에 없거나, 형식이 틀립니다."); + throw new ExpiredJwtException(null, null, "header에 없거나, 형식이 틀립니다."); } try{ token = authHeader.split(" ")[1].trim(); } catch (Exception e) { refreshTokenCheck(request, response, token); - throw new JwtException("액세스 토큰을 분리하는데 실패"); + throw new ExpiredJwtException(null, null, "액세스 토큰을 분리하는데 실패"); } if(!jwtTokenProvider.validateToken(token)){ @@ -87,8 +87,11 @@ private void refreshTokenCheck(HttpServletRequest request, HttpServletResponse r .orElse(null); } - token = memberService.reissue(refreshToken); - + try { + token = memberService.reissue(refreshToken); + } catch (Exception e){ + throw new JwtException("리프레시 토큰 검증 실패"); + } response.addHeader(JwtTokenProvider.AUTHORIZATION_HEADER, token); } diff --git a/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtExceptionFilter.java b/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtExceptionFilter.java index 601b551..4d26e9a 100644 --- a/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtExceptionFilter.java +++ b/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtExceptionFilter.java @@ -24,7 +24,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse try { filterChain.doFilter(request, response); } catch (ExpiredJwtException e) { - setErrorResponse(response, ErrorCode.TOKEN_EXPIRED); + setErrorResponse(response, ErrorCode.ACCESS_TOKEN_REFRESH); } catch (JwtException | IllegalArgumentException | NullPointerException | UnsupportedEncodingException e) { setErrorResponse(response, ErrorCode.NOT_VALID_TOKEN); } diff --git a/backend/src/main/java/com/ssafy/home/global/error/ErrorCode.java b/backend/src/main/java/com/ssafy/home/global/error/ErrorCode.java index 20a7b4d..940f442 100644 --- a/backend/src/main/java/com/ssafy/home/global/error/ErrorCode.java +++ b/backend/src/main/java/com/ssafy/home/global/error/ErrorCode.java @@ -17,6 +17,7 @@ public enum ErrorCode { REFRESH_TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, "A-006", "해당 refresh token은 만료됐습니다."), NOT_ACCESS_TOKEN_TYPE(HttpStatus.UNAUTHORIZED, "A-007", "해당 토큰은 ACCESS TOKEN이 아닙니다."), FORBIDDEN_ADMIN(HttpStatus.FORBIDDEN, "A-008", "관리자 Role이 아닙니다."), + ACCESS_TOKEN_REFRESH(HttpStatus.UNAUTHORIZED, "A-009", "액세스 토큰 재발급 하였습니다."), // 회원 INVALID_MEMBER_TYPE(HttpStatus.BAD_REQUEST, "M-001", "잘못된 회원 타입 입니다.(memberType : KAKAO)"), From 78735f532c78ce410f6faac254a4bb06a22049fd Mon Sep 17 00:00:00 2001 From: chwangmin Date: Thu, 23 May 2024 00:04:14 +0900 Subject: [PATCH 3/4] =?UTF-8?q?refactor=20:=20security=20path=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC,=20accessToken=20=EC=9E=AC=EB=B0=9C=EA=B8=89=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/filter/JwtAuthenticationFilter.java | 9 +++++---- .../global/auth/filter/JwtExceptionFilter.java | 4 ++-- .../config/security/WebSecurityConfiguration.java | 6 +++--- .../global/config/security/WebSecurityPath.java | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 9 deletions(-) create mode 100644 backend/src/main/java/com/ssafy/home/global/config/security/WebSecurityPath.java diff --git a/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtAuthenticationFilter.java b/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtAuthenticationFilter.java index ed40a08..2333065 100644 --- a/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtAuthenticationFilter.java +++ b/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtAuthenticationFilter.java @@ -4,7 +4,7 @@ import com.ssafy.home.entity.member.Member; import com.ssafy.home.global.auth.dto.MemberDto; import com.ssafy.home.global.auth.jwt.JwtTokenProvider; -import io.jsonwebtoken.ExpiredJwtException; +import com.ssafy.home.global.error.ErrorCode; import io.jsonwebtoken.JwtException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -17,6 +17,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.web.filter.OncePerRequestFilter; +import com.ssafy.home.global.error.exception.AuthenticationException; import java.io.IOException; import java.util.Arrays; @@ -41,19 +42,19 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse if(authHeader == null || !authHeader.startsWith("Bearer ")) { refreshTokenCheck(request, response, token); - throw new ExpiredJwtException(null, null, "header에 없거나, 형식이 틀립니다."); + throw new AuthenticationException(ErrorCode.NOT_EXISTS_AUTHORIZATION); } try{ token = authHeader.split(" ")[1].trim(); } catch (Exception e) { refreshTokenCheck(request, response, token); - throw new ExpiredJwtException(null, null, "액세스 토큰을 분리하는데 실패"); + throw new AuthenticationException(ErrorCode.NOT_VALID_TOKEN); } if(!jwtTokenProvider.validateToken(token)){ refreshTokenCheck(request, response, token); - throw new ExpiredJwtException(null, null, "액세스 토큰 만료"); + throw new AuthenticationException(ErrorCode.TOKEN_EXPIRED); } Long userId = jwtTokenProvider.getInfoId(token); diff --git a/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtExceptionFilter.java b/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtExceptionFilter.java index 4d26e9a..073f270 100644 --- a/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtExceptionFilter.java +++ b/backend/src/main/java/com/ssafy/home/global/auth/filter/JwtExceptionFilter.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.ssafy.home.global.error.ErrorCode; import com.ssafy.home.global.error.ErrorResponse; -import io.jsonwebtoken.ExpiredJwtException; +import com.ssafy.home.global.error.exception.AuthenticationException; import io.jsonwebtoken.JwtException; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; @@ -23,7 +23,7 @@ public class JwtExceptionFilter extends OncePerRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { filterChain.doFilter(request, response); - } catch (ExpiredJwtException e) { + } catch (AuthenticationException e) { setErrorResponse(response, ErrorCode.ACCESS_TOKEN_REFRESH); } catch (JwtException | IllegalArgumentException | NullPointerException | UnsupportedEncodingException e) { setErrorResponse(response, ErrorCode.NOT_VALID_TOKEN); diff --git a/backend/src/main/java/com/ssafy/home/global/config/security/WebSecurityConfiguration.java b/backend/src/main/java/com/ssafy/home/global/config/security/WebSecurityConfiguration.java index 52ea6db..48cfb59 100644 --- a/backend/src/main/java/com/ssafy/home/global/config/security/WebSecurityConfiguration.java +++ b/backend/src/main/java/com/ssafy/home/global/config/security/WebSecurityConfiguration.java @@ -25,12 +25,12 @@ public class WebSecurityConfiguration implements WebMvcConfigurer { private final JwtTokenProvider jwtTokenProvider; private final MemberService memberService; - private final String[] REQUIRE_AUTH_PATH = {"/health/**", "member/logout", "member/info", "member/pw", "/board/**", "/comment/**"}; + @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http - .securityMatchers((auth) -> auth.requestMatchers(REQUIRE_AUTH_PATH)); + .securityMatchers((auth) -> auth.requestMatchers(WebSecurityPath.REQUIRE_AUTH_PATH.getPaths())); http .csrf(AbstractHttpConfigurer::disable) .sessionManagement((session) -> session @@ -60,6 +60,6 @@ public WebSecurityCustomizer webSecurityCustomizer() { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new UserActivationInterceptor(memberService)) - .addPathPatterns(REQUIRE_AUTH_PATH); + .addPathPatterns(WebSecurityPath.REQUIRE_AUTH_PATH.getPaths()); } } diff --git a/backend/src/main/java/com/ssafy/home/global/config/security/WebSecurityPath.java b/backend/src/main/java/com/ssafy/home/global/config/security/WebSecurityPath.java new file mode 100644 index 0000000..a26a2e6 --- /dev/null +++ b/backend/src/main/java/com/ssafy/home/global/config/security/WebSecurityPath.java @@ -0,0 +1,14 @@ +package com.ssafy.home.global.config.security; + +import lombok.Getter; + +@Getter +public enum WebSecurityPath { + REQUIRE_AUTH_PATH("/health/**", "member/logout", "member/info", "member/pw", "/board/**", "/comment/**"); + + private final String[] paths; + + WebSecurityPath(String... paths) { + this.paths = paths; + } +} From 9ef3fe0f8850aabe038ace7c25c305fa3374a670 Mon Sep 17 00:00:00 2001 From: chwangmin Date: Thu, 23 May 2024 00:36:47 +0900 Subject: [PATCH 4/4] =?UTF-8?q?refactor=20:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=ED=9A=9F=EC=88=98=205=EB=B2=88=20=EC=B4=88=EA=B3=BC=20?= =?UTF-8?q?=ED=95=9C=20=EC=9C=A0=EC=A0=80=20=EC=A4=91=2030=EB=B6=84=20?= =?UTF-8?q?=EC=A7=80=EB=82=9C=20=EC=9C=A0=EC=A0=80=EC=9D=98=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=EB=A7=8C=20=EC=B4=88=EA=B8=B0=ED=99=94=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20-=20batch=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=200=EB=B6=84=20=EB=A7=88=EB=8B=A4=20-=20=EB=A7=A4=201?= =?UTF-8?q?=EB=B6=84=EB=A7=88=EB=8B=A4=20=EC=B2=B4=ED=81=AC=EC=8B=9C=20?= =?UTF-8?q?=EB=A7=88=EC=A7=80=EB=A7=89=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=8B=9C=EB=8F=84=2030=EB=B6=84=20=EC=A7=80=EB=82=AC=EB=8A=94?= =?UTF-8?q?=20=EC=A7=80=20=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20filter=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20-=20errorCode=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EB=B3=80=EA=B2=BD=20->=2030=EB=B6=84=20?= =?UTF-8?q?=ED=9B=84=20=EB=8B=A4=EC=8B=9C=20=EC=8B=9C=EB=8F=84=20-=20Local?= =?UTF-8?q?Date=20->=20LocalDateTime?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 4 ++++ .../home/domain/member/service/MemberService.java | 15 +++++++++++---- .../ssafy/home/entity/member/LoginAttempt.java | 6 +++--- .../java/com/ssafy/home/entity/member/Member.java | 3 ++- .../config/batch/ScheduledJobConfiguration.java | 2 +- .../ssafy/home/global/entity/BaseTimeEntity.java | 3 ++- .../com/ssafy/home/global/error/ErrorCode.java | 2 +- 7 files changed, 24 insertions(+), 11 deletions(-) diff --git a/backend/src/main/java/com/ssafy/home/domain/member/controller/MemberController.java b/backend/src/main/java/com/ssafy/home/domain/member/controller/MemberController.java index de1686a..4610e1d 100644 --- a/backend/src/main/java/com/ssafy/home/domain/member/controller/MemberController.java +++ b/backend/src/main/java/com/ssafy/home/domain/member/controller/MemberController.java @@ -50,6 +50,10 @@ public ResponseEntity login(@RequestBody LoginRequest loginRequest, Http TokenResponse tokenResponse = memberService.login(loginRequest.getEmail(), loginRequest.getPassword()); + if(tokenResponse == null){ + throw new AuthenticationException(ErrorCode.MEMBER_NOT_MATCH); + } + response.addHeader(JwtTokenProvider.AUTHORIZATION_HEADER, tokenResponse.getAccessToken()); Cookie cookie = new Cookie("refreshToken", tokenResponse.getRefreshToken()); diff --git a/backend/src/main/java/com/ssafy/home/domain/member/service/MemberService.java b/backend/src/main/java/com/ssafy/home/domain/member/service/MemberService.java index 680f619..2bb644a 100644 --- a/backend/src/main/java/com/ssafy/home/domain/member/service/MemberService.java +++ b/backend/src/main/java/com/ssafy/home/domain/member/service/MemberService.java @@ -26,6 +26,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import java.util.List; + @Service @RequiredArgsConstructor @Slf4j @@ -103,7 +106,7 @@ public TokenResponse login(String email, String password) { } catch (AuthenticationException e){ member.getLoginAttempt().updateCount(); - throw new AuthenticationException(ErrorCode.MEMBER_NOT_MATCH); + e.printStackTrace(); } catch (Exception e) { member.getLoginAttempt().updateCount(); e.printStackTrace(); @@ -183,9 +186,13 @@ public void sendPassword(String email) { } @Transactional - public void initAttempt(){ - loginAttemptRepository.findAll().stream() + public void initAttempt() { + LocalDateTime thirtyMinutesAgo = LocalDateTime.now().minusMinutes(30); + List staleAttempts = loginAttemptRepository.findAll().stream() .filter(loginAttempt -> loginAttempt.getCount() >= 5) - .forEach(LoginAttempt::initCount); + .filter(loginAttempt -> loginAttempt.getLoginRecentAttemp().isBefore(thirtyMinutesAgo)) + .toList(); + + staleAttempts.forEach(LoginAttempt::initCount); } } diff --git a/backend/src/main/java/com/ssafy/home/entity/member/LoginAttempt.java b/backend/src/main/java/com/ssafy/home/entity/member/LoginAttempt.java index 25dd89f..10ccde1 100644 --- a/backend/src/main/java/com/ssafy/home/entity/member/LoginAttempt.java +++ b/backend/src/main/java/com/ssafy/home/entity/member/LoginAttempt.java @@ -8,7 +8,7 @@ import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import java.time.LocalDate; +import java.time.LocalDateTime; @Entity @NoArgsConstructor @@ -27,7 +27,7 @@ public class LoginAttempt { @LastModifiedDate @Column(name = "login_recent_attemp", columnDefinition = "datetime default CURRENT_TIMESTAMP") - private LocalDate loginRecentAttemp; + private LocalDateTime loginRecentAttemp; @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") @@ -39,7 +39,7 @@ public LoginAttempt(Member member) { } public void updateCount(){ - this.count++; + this.count = this.count + 1; } public void initCount(){ diff --git a/backend/src/main/java/com/ssafy/home/entity/member/Member.java b/backend/src/main/java/com/ssafy/home/entity/member/Member.java index a6131e4..30f0889 100644 --- a/backend/src/main/java/com/ssafy/home/entity/member/Member.java +++ b/backend/src/main/java/com/ssafy/home/entity/member/Member.java @@ -10,6 +10,7 @@ import org.springframework.data.jpa.domain.support.AuditingEntityListener; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.List; @NoArgsConstructor @@ -41,7 +42,7 @@ public class Member { @LastModifiedDate @Column(name = "modify_date", columnDefinition = "datetime default CURRENT_TIMESTAMP") - private LocalDate modifyDate; + private LocalDateTime modifyDate; @Column(name = "is_deleted") @ColumnDefault("false") diff --git a/backend/src/main/java/com/ssafy/home/global/config/batch/ScheduledJobConfiguration.java b/backend/src/main/java/com/ssafy/home/global/config/batch/ScheduledJobConfiguration.java index 5d4627e..4003747 100644 --- a/backend/src/main/java/com/ssafy/home/global/config/batch/ScheduledJobConfiguration.java +++ b/backend/src/main/java/com/ssafy/home/global/config/batch/ScheduledJobConfiguration.java @@ -12,7 +12,7 @@ public class ScheduledJobConfiguration { private final MemberService memberService; - @Scheduled(cron ="0 30 * * * *", zone = "Asia/Seoul") + @Scheduled(cron ="0 * * * * *", zone = "Asia/Seoul") public void scheduledEndForm() { memberService.initAttempt(); } diff --git a/backend/src/main/java/com/ssafy/home/global/entity/BaseTimeEntity.java b/backend/src/main/java/com/ssafy/home/global/entity/BaseTimeEntity.java index 37c7ac0..894a5a6 100644 --- a/backend/src/main/java/com/ssafy/home/global/entity/BaseTimeEntity.java +++ b/backend/src/main/java/com/ssafy/home/global/entity/BaseTimeEntity.java @@ -2,12 +2,13 @@ import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; -import java.time.LocalDateTime; import lombok.Getter; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import java.time.LocalDateTime; + @Getter @EntityListeners(AuditingEntityListener.class) @MappedSuperclass diff --git a/backend/src/main/java/com/ssafy/home/global/error/ErrorCode.java b/backend/src/main/java/com/ssafy/home/global/error/ErrorCode.java index 940f442..8fcd18f 100644 --- a/backend/src/main/java/com/ssafy/home/global/error/ErrorCode.java +++ b/backend/src/main/java/com/ssafy/home/global/error/ErrorCode.java @@ -23,7 +23,7 @@ public enum ErrorCode { INVALID_MEMBER_TYPE(HttpStatus.BAD_REQUEST, "M-001", "잘못된 회원 타입 입니다.(memberType : KAKAO)"), ALREADY_REGISTERED_MEMBER(HttpStatus.BAD_REQUEST, "M-002", "이미 가입된 회원 입니다."), MEMBER_NOT_EXISTS(HttpStatus.BAD_REQUEST, "M-003", "해당 회원은 존재하지 않습니다."), - MEMBER_COUNT_OUT(HttpStatus.BAD_REQUEST, "M-004", "해당 회원 로그인 시도 횟수가 초과되었습니다. (비밀번호 변경이 필요합니다.)"), + MEMBER_COUNT_OUT(HttpStatus.BAD_REQUEST, "M-004", "해당 회원 로그인 시도 횟수가 초과되었습니다. 30분 후 다시 시도하세요!"), MEMBER_NOT_MATCH(HttpStatus.BAD_REQUEST, "M-005", " 아이디(로그인 전용 아이디) 또는 비밀번호를 잘못 입력했습니다.\n" + "입력하신 내용을 다시 확인해주세요."),