From 1285c103d449b7f2ef9430f8d553e069227589f0 Mon Sep 17 00:00:00 2001 From: mng990 Date: Tue, 22 Oct 2024 02:14:08 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=201.=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EA=B5=AC=ED=98=84=202.=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EA=B5=AC=ED=98=84=203.=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EA=B5=AC=EC=A1=B0=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 --- .idea/misc.xml | 1 + .../cors => auth/config}/CorsConfig.java | 4 +- .../config}/PropertiesConfig.java | 8 +- .../config}/SecurityConfig.java | 16 ++-- .../exception/ApiException.java | 2 +- .../{common => auth}/exception/ErrorCode.java | 2 +- .../properties/CorsProperties.java | 2 +- .../properties/JwtProperties.java | 2 +- .../properties/SecurityProperties.java | 2 +- .../ExceptionHandlingFilter.java | 10 +-- .../jwtFilter/JwtAuthenticationFilter.java | 12 +-- .../security}/jwtFilter/JwtDTO.java | 2 +- .../security}/jwtFilter/JwtTokenProvider.java | 13 ++- .../jwtFilter/JwtTokenProviderImpl.java | 82 ++++++++----------- .../loginFilter/CustomUserDetailsService.java | 10 +-- .../security}/loginFilter/LoginFilter.java | 19 ++--- .../domains/member/MemberController.java | 34 -------- .../backend/domains/member/domain/Logout.java | 19 ----- .../backend/domains/member/domain/Member.java | 43 ---------- .../member/repository/LogoutRepository.java | 12 --- .../repository/RefreshTokenRepository.java | 16 ---- .../domains/member/service/LogoutService.java | 24 ------ .../domains/member/service/MemberService.java | 40 --------- .../member/controller/MemberController.java | 58 +++++++++++++ .../backend/member/dto/DeleteRequest.java | 7 ++ .../backend/member/dto/SignUpRequest.java | 17 ++++ .../aiin/backend/member/entity/Member.java | 80 ++++++++++++++++++ .../backend/member/entity/ProfileImage.java | 32 ++++++++ .../entity}/RefreshToken.java | 6 +- .../memberLoader/MemberLoader.java | 10 +-- .../aiin/backend/member/model/Gender.java | 10 +++ .../domain => member/model}/MemberRole.java | 2 +- .../aiin/backend/member/model/Provider.java | 11 +++ .../member/repository/MemberRepository.java | 6 +- .../repository/RefreshTokenRepository.java | 16 ++++ .../backend/member/service/LogoutService.java | 19 +++++ .../backend/member/service/MemberService.java | 54 ++++++++++++ .../member/service/RefreshTokenService.java | 17 ++-- .../{common => util}/dto/BaseResponse.java | 2 +- .../{common => util}/dto/DataResponse.java | 4 +- .../{common => util}/dto/ErrorResponse.java | 4 +- .../util/encoder/PasswordEncoderConfig.java | 2 +- .../util/encoder/PasswordEncoderUtil.java | 2 +- .../{common => util}/entity/BaseEntity.java | 2 +- .../util/responseWriter/ResponseWriter.java | 4 +- 45 files changed, 423 insertions(+), 317 deletions(-) rename backend/src/main/java/aiin/backend/{common/cors => auth/config}/CorsConfig.java (91%) rename backend/src/main/java/aiin/backend/{common/properties => auth/config}/PropertiesConfig.java (56%) rename backend/src/main/java/aiin/backend/{common/security => auth/config}/SecurityConfig.java (88%) rename backend/src/main/java/aiin/backend/{common => auth}/exception/ApiException.java (94%) rename backend/src/main/java/aiin/backend/{common => auth}/exception/ErrorCode.java (97%) rename backend/src/main/java/aiin/backend/{common/properties => auth}/properties/CorsProperties.java (89%) rename backend/src/main/java/aiin/backend/{common/properties => auth}/properties/JwtProperties.java (93%) rename backend/src/main/java/aiin/backend/{common/properties => auth}/properties/SecurityProperties.java (84%) rename backend/src/main/java/aiin/backend/{common/security/filter => auth/security}/exceptionHandlingFilter/ExceptionHandlingFilter.java (81%) rename backend/src/main/java/aiin/backend/{common/security/filter => auth/security}/jwtFilter/JwtAuthenticationFilter.java (91%) rename backend/src/main/java/aiin/backend/{common/security/filter => auth/security}/jwtFilter/JwtDTO.java (92%) rename backend/src/main/java/aiin/backend/{common/security/filter => auth/security}/jwtFilter/JwtTokenProvider.java (78%) rename backend/src/main/java/aiin/backend/{common/security/filter => auth/security}/jwtFilter/JwtTokenProviderImpl.java (72%) rename backend/src/main/java/aiin/backend/{common/security/filter => auth/security}/loginFilter/CustomUserDetailsService.java (78%) rename backend/src/main/java/aiin/backend/{common/security/filter => auth/security}/loginFilter/LoginFilter.java (83%) delete mode 100644 backend/src/main/java/aiin/backend/domains/member/MemberController.java delete mode 100644 backend/src/main/java/aiin/backend/domains/member/domain/Logout.java delete mode 100644 backend/src/main/java/aiin/backend/domains/member/domain/Member.java delete mode 100644 backend/src/main/java/aiin/backend/domains/member/repository/LogoutRepository.java delete mode 100644 backend/src/main/java/aiin/backend/domains/member/repository/RefreshTokenRepository.java delete mode 100644 backend/src/main/java/aiin/backend/domains/member/service/LogoutService.java delete mode 100644 backend/src/main/java/aiin/backend/domains/member/service/MemberService.java create mode 100644 backend/src/main/java/aiin/backend/member/controller/MemberController.java create mode 100644 backend/src/main/java/aiin/backend/member/dto/DeleteRequest.java create mode 100644 backend/src/main/java/aiin/backend/member/dto/SignUpRequest.java create mode 100644 backend/src/main/java/aiin/backend/member/entity/Member.java create mode 100644 backend/src/main/java/aiin/backend/member/entity/ProfileImage.java rename backend/src/main/java/aiin/backend/{domains/member/domain => member/entity}/RefreshToken.java (80%) rename backend/src/main/java/aiin/backend/{common/util => member}/memberLoader/MemberLoader.java (74%) create mode 100644 backend/src/main/java/aiin/backend/member/model/Gender.java rename backend/src/main/java/aiin/backend/{domains/member/domain => member/model}/MemberRole.java (79%) create mode 100644 backend/src/main/java/aiin/backend/member/model/Provider.java rename backend/src/main/java/aiin/backend/{domains => }/member/repository/MemberRepository.java (62%) create mode 100644 backend/src/main/java/aiin/backend/member/repository/RefreshTokenRepository.java create mode 100644 backend/src/main/java/aiin/backend/member/service/LogoutService.java create mode 100644 backend/src/main/java/aiin/backend/member/service/MemberService.java rename backend/src/main/java/aiin/backend/{domains => }/member/service/RefreshTokenService.java (52%) rename backend/src/main/java/aiin/backend/{common => util}/dto/BaseResponse.java (93%) rename backend/src/main/java/aiin/backend/{common => util}/dto/DataResponse.java (69%) rename backend/src/main/java/aiin/backend/{common => util}/dto/ErrorResponse.java (93%) rename backend/src/main/java/aiin/backend/{common => }/util/encoder/PasswordEncoderConfig.java (91%) rename backend/src/main/java/aiin/backend/{common => }/util/encoder/PasswordEncoderUtil.java (90%) rename backend/src/main/java/aiin/backend/{common => util}/entity/BaseEntity.java (94%) rename backend/src/main/java/aiin/backend/{common => }/util/responseWriter/ResponseWriter.java (91%) diff --git a/.idea/misc.xml b/.idea/misc.xml index 8ed2205..c1c2a99 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ + \ No newline at end of file diff --git a/backend/src/main/java/aiin/backend/common/cors/CorsConfig.java b/backend/src/main/java/aiin/backend/auth/config/CorsConfig.java similarity index 91% rename from backend/src/main/java/aiin/backend/common/cors/CorsConfig.java rename to backend/src/main/java/aiin/backend/auth/config/CorsConfig.java index bc39660..f5bb996 100644 --- a/backend/src/main/java/aiin/backend/common/cors/CorsConfig.java +++ b/backend/src/main/java/aiin/backend/auth/config/CorsConfig.java @@ -1,11 +1,11 @@ -package aiin.backend.common.cors; +package aiin.backend.auth.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import aiin.backend.common.properties.properties.CorsProperties; +import aiin.backend.auth.properties.CorsProperties; import lombok.RequiredArgsConstructor; @Configuration diff --git a/backend/src/main/java/aiin/backend/common/properties/PropertiesConfig.java b/backend/src/main/java/aiin/backend/auth/config/PropertiesConfig.java similarity index 56% rename from backend/src/main/java/aiin/backend/common/properties/PropertiesConfig.java rename to backend/src/main/java/aiin/backend/auth/config/PropertiesConfig.java index 65c5e42..fea6a92 100644 --- a/backend/src/main/java/aiin/backend/common/properties/PropertiesConfig.java +++ b/backend/src/main/java/aiin/backend/auth/config/PropertiesConfig.java @@ -1,11 +1,11 @@ -package aiin.backend.common.properties; +package aiin.backend.auth.config; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; -import aiin.backend.common.properties.properties.CorsProperties; -import aiin.backend.common.properties.properties.JwtProperties; -import aiin.backend.common.properties.properties.SecurityProperties; +import aiin.backend.auth.properties.CorsProperties; +import aiin.backend.auth.properties.JwtProperties; +import aiin.backend.auth.properties.SecurityProperties; @Configuration @EnableConfigurationProperties(value = { diff --git a/backend/src/main/java/aiin/backend/common/security/SecurityConfig.java b/backend/src/main/java/aiin/backend/auth/config/SecurityConfig.java similarity index 88% rename from backend/src/main/java/aiin/backend/common/security/SecurityConfig.java rename to backend/src/main/java/aiin/backend/auth/config/SecurityConfig.java index dce44fb..6042b3c 100644 --- a/backend/src/main/java/aiin/backend/common/security/SecurityConfig.java +++ b/backend/src/main/java/aiin/backend/auth/config/SecurityConfig.java @@ -1,11 +1,11 @@ -package aiin.backend.common.security; - -import aiin.backend.common.properties.properties.SecurityProperties; -import aiin.backend.common.security.filter.exceptionHandlingFilter.ExceptionHandlingFilter; -import aiin.backend.common.security.filter.jwtFilter.JwtAuthenticationFilter; -import aiin.backend.common.security.filter.jwtFilter.JwtTokenProvider; -import aiin.backend.common.security.filter.loginFilter.LoginFilter; -import aiin.backend.domains.member.service.MemberService; +package aiin.backend.auth.config; + +import aiin.backend.auth.properties.SecurityProperties; +import aiin.backend.auth.security.exceptionHandlingFilter.ExceptionHandlingFilter; +import aiin.backend.auth.security.jwtFilter.JwtAuthenticationFilter; +import aiin.backend.auth.security.jwtFilter.JwtTokenProvider; +import aiin.backend.auth.security.loginFilter.LoginFilter; +import aiin.backend.member.service.MemberService; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/backend/src/main/java/aiin/backend/common/exception/ApiException.java b/backend/src/main/java/aiin/backend/auth/exception/ApiException.java similarity index 94% rename from backend/src/main/java/aiin/backend/common/exception/ApiException.java rename to backend/src/main/java/aiin/backend/auth/exception/ApiException.java index dfc6c35..be2d42a 100644 --- a/backend/src/main/java/aiin/backend/common/exception/ApiException.java +++ b/backend/src/main/java/aiin/backend/auth/exception/ApiException.java @@ -1,4 +1,4 @@ -package aiin.backend.common.exception; +package aiin.backend.auth.exception; import lombok.Getter; diff --git a/backend/src/main/java/aiin/backend/common/exception/ErrorCode.java b/backend/src/main/java/aiin/backend/auth/exception/ErrorCode.java similarity index 97% rename from backend/src/main/java/aiin/backend/common/exception/ErrorCode.java rename to backend/src/main/java/aiin/backend/auth/exception/ErrorCode.java index 85becb5..3114048 100644 --- a/backend/src/main/java/aiin/backend/common/exception/ErrorCode.java +++ b/backend/src/main/java/aiin/backend/auth/exception/ErrorCode.java @@ -1,4 +1,4 @@ -package aiin.backend.common.exception; +package aiin.backend.auth.exception; import org.springframework.http.HttpStatus; diff --git a/backend/src/main/java/aiin/backend/common/properties/properties/CorsProperties.java b/backend/src/main/java/aiin/backend/auth/properties/CorsProperties.java similarity index 89% rename from backend/src/main/java/aiin/backend/common/properties/properties/CorsProperties.java rename to backend/src/main/java/aiin/backend/auth/properties/CorsProperties.java index b4bb2c2..686d33b 100644 --- a/backend/src/main/java/aiin/backend/common/properties/properties/CorsProperties.java +++ b/backend/src/main/java/aiin/backend/auth/properties/CorsProperties.java @@ -1,4 +1,4 @@ -package aiin.backend.common.properties.properties; +package aiin.backend.auth.properties; import java.util.List; diff --git a/backend/src/main/java/aiin/backend/common/properties/properties/JwtProperties.java b/backend/src/main/java/aiin/backend/auth/properties/JwtProperties.java similarity index 93% rename from backend/src/main/java/aiin/backend/common/properties/properties/JwtProperties.java rename to backend/src/main/java/aiin/backend/auth/properties/JwtProperties.java index f233f44..b49bc7e 100644 --- a/backend/src/main/java/aiin/backend/common/properties/properties/JwtProperties.java +++ b/backend/src/main/java/aiin/backend/auth/properties/JwtProperties.java @@ -1,4 +1,4 @@ -package aiin.backend.common.properties.properties; +package aiin.backend.auth.properties; import lombok.Getter; import lombok.Setter; diff --git a/backend/src/main/java/aiin/backend/common/properties/properties/SecurityProperties.java b/backend/src/main/java/aiin/backend/auth/properties/SecurityProperties.java similarity index 84% rename from backend/src/main/java/aiin/backend/common/properties/properties/SecurityProperties.java rename to backend/src/main/java/aiin/backend/auth/properties/SecurityProperties.java index d2f14cb..2d71dc0 100644 --- a/backend/src/main/java/aiin/backend/common/properties/properties/SecurityProperties.java +++ b/backend/src/main/java/aiin/backend/auth/properties/SecurityProperties.java @@ -1,4 +1,4 @@ -package aiin.backend.common.properties.properties; +package aiin.backend.auth.properties; import lombok.Getter; import lombok.Setter; diff --git a/backend/src/main/java/aiin/backend/common/security/filter/exceptionHandlingFilter/ExceptionHandlingFilter.java b/backend/src/main/java/aiin/backend/auth/security/exceptionHandlingFilter/ExceptionHandlingFilter.java similarity index 81% rename from backend/src/main/java/aiin/backend/common/security/filter/exceptionHandlingFilter/ExceptionHandlingFilter.java rename to backend/src/main/java/aiin/backend/auth/security/exceptionHandlingFilter/ExceptionHandlingFilter.java index f4702ee..50c9fe5 100644 --- a/backend/src/main/java/aiin/backend/common/security/filter/exceptionHandlingFilter/ExceptionHandlingFilter.java +++ b/backend/src/main/java/aiin/backend/auth/security/exceptionHandlingFilter/ExceptionHandlingFilter.java @@ -1,9 +1,9 @@ -package aiin.backend.common.security.filter.exceptionHandlingFilter; +package aiin.backend.auth.security.exceptionHandlingFilter; -import aiin.backend.common.dto.ErrorResponse; -import aiin.backend.common.exception.ApiException; -import aiin.backend.common.exception.ErrorCode; -import aiin.backend.common.util.responseWriter.ResponseWriter; +import aiin.backend.util.dto.ErrorResponse; +import aiin.backend.auth.exception.ApiException; +import aiin.backend.auth.exception.ErrorCode; +import aiin.backend.util.responseWriter.ResponseWriter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; diff --git a/backend/src/main/java/aiin/backend/common/security/filter/jwtFilter/JwtAuthenticationFilter.java b/backend/src/main/java/aiin/backend/auth/security/jwtFilter/JwtAuthenticationFilter.java similarity index 91% rename from backend/src/main/java/aiin/backend/common/security/filter/jwtFilter/JwtAuthenticationFilter.java rename to backend/src/main/java/aiin/backend/auth/security/jwtFilter/JwtAuthenticationFilter.java index 6c321d1..34fe29e 100644 --- a/backend/src/main/java/aiin/backend/common/security/filter/jwtFilter/JwtAuthenticationFilter.java +++ b/backend/src/main/java/aiin/backend/auth/security/jwtFilter/JwtAuthenticationFilter.java @@ -1,10 +1,10 @@ -package aiin.backend.common.security.filter.jwtFilter; +package aiin.backend.auth.security.jwtFilter; -import static aiin.backend.common.exception.ErrorCode.*; +import static aiin.backend.auth.exception.ErrorCode.*; -import aiin.backend.common.exception.ApiException; -import aiin.backend.common.exception.ErrorCode; -import aiin.backend.common.properties.properties.SecurityProperties; +import aiin.backend.auth.exception.ApiException; +import aiin.backend.auth.exception.ErrorCode; +import aiin.backend.auth.properties.SecurityProperties; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -56,7 +56,7 @@ protected void doFilterInternal( //5. 그 외 모든 경우는 에러 리턴 if (refreshToken != null && jwtTokenProvider.isTokenValid(refreshToken)) { log.info("refresh토큰 인증 성공"); - jwtTokenProvider.checkRefreshTokenAndReIssueAccessAndRefreshToken(response, refreshToken); + jwtTokenProvider.checkRefreshTokenAndReIssueAccessAndRefreshToken(response, accessToken, refreshToken); } else if (refreshToken != null && !jwtTokenProvider.isTokenValid(refreshToken)) { log.info("refresh토큰 인증 실패"); throw ApiException.from(INVALID_REFRESH_TOKEN); diff --git a/backend/src/main/java/aiin/backend/common/security/filter/jwtFilter/JwtDTO.java b/backend/src/main/java/aiin/backend/auth/security/jwtFilter/JwtDTO.java similarity index 92% rename from backend/src/main/java/aiin/backend/common/security/filter/jwtFilter/JwtDTO.java rename to backend/src/main/java/aiin/backend/auth/security/jwtFilter/JwtDTO.java index 62a197b..5ec3857 100644 --- a/backend/src/main/java/aiin/backend/common/security/filter/jwtFilter/JwtDTO.java +++ b/backend/src/main/java/aiin/backend/auth/security/jwtFilter/JwtDTO.java @@ -1,4 +1,4 @@ -package aiin.backend.common.security.filter.jwtFilter; +package aiin.backend.auth.security.jwtFilter; import lombok.AccessLevel; import lombok.AllArgsConstructor; diff --git a/backend/src/main/java/aiin/backend/common/security/filter/jwtFilter/JwtTokenProvider.java b/backend/src/main/java/aiin/backend/auth/security/jwtFilter/JwtTokenProvider.java similarity index 78% rename from backend/src/main/java/aiin/backend/common/security/filter/jwtFilter/JwtTokenProvider.java rename to backend/src/main/java/aiin/backend/auth/security/jwtFilter/JwtTokenProvider.java index 212d629..1e65b45 100644 --- a/backend/src/main/java/aiin/backend/common/security/filter/jwtFilter/JwtTokenProvider.java +++ b/backend/src/main/java/aiin/backend/auth/security/jwtFilter/JwtTokenProvider.java @@ -1,9 +1,10 @@ -package aiin.backend.common.security.filter.jwtFilter; +package aiin.backend.auth.security.jwtFilter; import io.jsonwebtoken.Claims; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; +import org.springframework.transaction.annotation.Transactional; import java.util.Optional; @@ -13,11 +14,7 @@ public interface JwtTokenProvider { String createAccessToken(Long memberId); - String createRefreshToken(); - - void updateRefreshToken(Long memberId, String refreshToken); - - void destroyRefreshToken(Long memberId); + String createRefreshToken(Long memberId); void sendAccessAndRefreshToken(HttpServletResponse response, String accessToken, String refreshToken); @@ -37,7 +34,9 @@ public interface JwtTokenProvider { boolean isTokenValid(String token); - void checkRefreshTokenAndReIssueAccessAndRefreshToken(HttpServletResponse response, String refreshToken); + void expireRefreshToken(Long memberId, String accessToken, String refreshToken); + + void checkRefreshTokenAndReIssueAccessAndRefreshToken(HttpServletResponse response, String accessToken, String refreshToken); boolean isLogout(String accessToken); } \ No newline at end of file diff --git a/backend/src/main/java/aiin/backend/common/security/filter/jwtFilter/JwtTokenProviderImpl.java b/backend/src/main/java/aiin/backend/auth/security/jwtFilter/JwtTokenProviderImpl.java similarity index 72% rename from backend/src/main/java/aiin/backend/common/security/filter/jwtFilter/JwtTokenProviderImpl.java rename to backend/src/main/java/aiin/backend/auth/security/jwtFilter/JwtTokenProviderImpl.java index 5daba0e..0714d74 100644 --- a/backend/src/main/java/aiin/backend/common/security/filter/jwtFilter/JwtTokenProviderImpl.java +++ b/backend/src/main/java/aiin/backend/auth/security/jwtFilter/JwtTokenProviderImpl.java @@ -1,18 +1,16 @@ -package aiin.backend.common.security.filter.jwtFilter; - -import static aiin.backend.common.exception.ErrorCode.*; - -import aiin.backend.common.dto.DataResponse; -import aiin.backend.common.exception.ApiException; -import aiin.backend.common.properties.properties.JwtProperties; -import aiin.backend.common.security.filter.jwtFilter.JwtDTO.AccessAndRefreshTokenResponse; -import aiin.backend.common.security.filter.jwtFilter.JwtDTO.AccessTokenResponse; -import aiin.backend.common.util.responseWriter.ResponseWriter; -import aiin.backend.domains.member.domain.Member; -import aiin.backend.domains.member.domain.RefreshToken; -import aiin.backend.domains.member.repository.MemberRepository; -import aiin.backend.domains.member.service.LogoutService; -import aiin.backend.domains.member.service.RefreshTokenService; +package aiin.backend.auth.security.jwtFilter; + +import static aiin.backend.auth.exception.ErrorCode.*; + +import aiin.backend.util.dto.DataResponse; +import aiin.backend.auth.exception.ApiException; +import aiin.backend.auth.properties.JwtProperties; +import aiin.backend.util.responseWriter.ResponseWriter; +import aiin.backend.member.entity.Member; +import aiin.backend.member.entity.RefreshToken; +import aiin.backend.member.repository.MemberRepository; +import aiin.backend.member.service.LogoutService; +import aiin.backend.member.service.RefreshTokenService; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; @@ -70,9 +68,9 @@ public Authentication getAuthentication(String accessToken) { .orElseThrow(() -> ApiException.from(MEMBER_NOT_FOUND)); String email = member.getEmail(); - String pw = member.getPw(); + String pw = member.getPassword(); - List authorities = List.of(new SimpleGrantedAuthority(member.getMemberRole().getValue())); + List authorities = List.of(new SimpleGrantedAuthority(member.getRole().getValue())); return UsernamePasswordAuthenticationToken.authenticated(email, pw, authorities); } @@ -88,9 +86,10 @@ public String createAccessToken(Long memberId) { } @Override - public String createRefreshToken() { + public String createRefreshToken(Long memberId) { return Jwts.builder() .setSubject(jwtProperties.getREFRESH_TOKEN_SUBJECT()) + .claim(jwtProperties.getMEMBER_ID_CLAIM(), memberId) .setExpiration(new Date(System.currentTimeMillis() + jwtProperties.getRefresh().getExpiration() * 1000L)) .signWith(key, SignatureAlgorithm.HS256) .compact(); @@ -98,25 +97,10 @@ public String createRefreshToken() { @Override @Transactional - public void updateRefreshToken(Long memberId, String refreshToken) { - Optional optionalRefreshToken = refreshTokenService.findByMemberId(memberId); - - // refreshToken이 없으면 생성 및 저장, 있으면 refreshToken 값 변경 - RefreshToken refreshTokenObj; - if (optionalRefreshToken.isEmpty()) { - refreshTokenObj = new RefreshToken(refreshToken, memberId); - } else { - refreshTokenObj = optionalRefreshToken.get(); - refreshTokenObj.setToken(refreshToken); - } - - refreshTokenService.save(refreshTokenObj); - } - - @Override - @Transactional - public void destroyRefreshToken(Long memberId) { - refreshTokenService.deleteByMemberId(memberId); + public void expireRefreshToken(Long memberId, String accessToken, String refreshToken) { + // refreshToken을 Black List에 저장 + RefreshToken expiredRefreshToken = new RefreshToken(accessToken, refreshToken); + refreshTokenService.save(expiredRefreshToken); } @Override @@ -124,7 +108,7 @@ public void sendAccessAndRefreshToken(HttpServletResponse response, String acces setAccessTokenHeader(response, accessToken); setRefreshTokenHeader(response, refreshToken); - AccessAndRefreshTokenResponse accessAndRefreshTokenResponse = AccessAndRefreshTokenResponse.from(accessToken, + JwtDTO.AccessAndRefreshTokenResponse accessAndRefreshTokenResponse = JwtDTO.AccessAndRefreshTokenResponse.from(accessToken, refreshToken); ResponseWriter.writeResponse(response, DataResponse.from(accessAndRefreshTokenResponse), HttpStatus.OK); @@ -134,7 +118,7 @@ public void sendAccessAndRefreshToken(HttpServletResponse response, String acces public void sendAccessToken(HttpServletResponse response, String accessToken) { setAccessTokenHeader(response, accessToken); - AccessTokenResponse accessTokenResponse = AccessTokenResponse.from(accessToken); + JwtDTO.AccessTokenResponse accessTokenResponse = JwtDTO.AccessTokenResponse.from(accessToken); ResponseWriter.writeResponse(response, DataResponse.from(accessTokenResponse), HttpStatus.OK); } @@ -207,24 +191,26 @@ public boolean isTokenValid(String token) { return false; } - public void checkRefreshTokenAndReIssueAccessAndRefreshToken(HttpServletResponse response, String refreshToken) { + public void checkRefreshTokenAndReIssueAccessAndRefreshToken(HttpServletResponse response, String accessToken, String refreshToken) { //refreshToken이 유효한지 확인 - RefreshToken refreshTokenObj = refreshTokenService.findByToken(refreshToken) - .orElseThrow(() -> ApiException.from(INVALID_REFRESH_TOKEN)); - Long memberId = refreshTokenObj.getMemberId(); + Long memberId = extractMemberId(refreshToken) + .orElseThrow(() -> ApiException.from(INVALID_REFRESH_TOKEN)); - String newAccessToken = createAccessToken(memberId); - String newRefreshToken = createRefreshToken(); + if(refreshTokenService.existsByAccessToken(accessToken)){ + throw ApiException.from(INVALID_REFRESH_TOKEN); + } - //기존 refreshToken 삭제 및 새로운 refreshToken 저장 - updateRefreshToken(memberId, newRefreshToken); + String newAccessToken = createAccessToken(memberId); + String newRefreshToken = createRefreshToken(memberId); + + expireRefreshToken(memberId, accessToken, refreshToken); sendAccessAndRefreshToken(response, newAccessToken, newRefreshToken); } @Override public boolean isLogout(String accessToken) { - return logoutService.existsByAccessToken(accessToken); + return !logoutService.existsByAccessToken(accessToken); } } \ No newline at end of file diff --git a/backend/src/main/java/aiin/backend/common/security/filter/loginFilter/CustomUserDetailsService.java b/backend/src/main/java/aiin/backend/auth/security/loginFilter/CustomUserDetailsService.java similarity index 78% rename from backend/src/main/java/aiin/backend/common/security/filter/loginFilter/CustomUserDetailsService.java rename to backend/src/main/java/aiin/backend/auth/security/loginFilter/CustomUserDetailsService.java index 5cb199d..e555468 100644 --- a/backend/src/main/java/aiin/backend/common/security/filter/loginFilter/CustomUserDetailsService.java +++ b/backend/src/main/java/aiin/backend/auth/security/loginFilter/CustomUserDetailsService.java @@ -1,4 +1,4 @@ -package aiin.backend.common.security.filter.loginFilter; +package aiin.backend.auth.security.loginFilter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; @@ -6,8 +6,8 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; -import aiin.backend.domains.member.domain.Member; -import aiin.backend.domains.member.service.MemberService; +import aiin.backend.member.entity.Member; +import aiin.backend.member.service.MemberService; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @@ -21,8 +21,8 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx Member member = memberService.findByEmail(username) .orElseThrow(() -> new UsernameNotFoundException("해당하는 회원을 찾을 수 없습니다.")); - String pw = member.getPw(); - String role = member.getMemberRole().getValue(); + String pw = member.getPassword(); + String role = member.getRole().getValue(); return User.builder() .username(username) diff --git a/backend/src/main/java/aiin/backend/common/security/filter/loginFilter/LoginFilter.java b/backend/src/main/java/aiin/backend/auth/security/loginFilter/LoginFilter.java similarity index 83% rename from backend/src/main/java/aiin/backend/common/security/filter/loginFilter/LoginFilter.java rename to backend/src/main/java/aiin/backend/auth/security/loginFilter/LoginFilter.java index 15fdcd4..114aa4b 100644 --- a/backend/src/main/java/aiin/backend/common/security/filter/loginFilter/LoginFilter.java +++ b/backend/src/main/java/aiin/backend/auth/security/loginFilter/LoginFilter.java @@ -1,6 +1,6 @@ -package aiin.backend.common.security.filter.loginFilter; +package aiin.backend.auth.security.loginFilter; -import static aiin.backend.common.exception.ErrorCode.*; +import static aiin.backend.auth.exception.ErrorCode.*; import org.springframework.http.HttpStatus; import org.springframework.lang.Nullable; @@ -10,12 +10,12 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import aiin.backend.common.dto.DataResponse; -import aiin.backend.common.exception.ApiException; -import aiin.backend.common.security.filter.jwtFilter.JwtTokenProvider; -import aiin.backend.common.util.responseWriter.ResponseWriter; -import aiin.backend.domains.member.domain.Member; -import aiin.backend.domains.member.service.MemberService; +import aiin.backend.auth.security.jwtFilter.JwtTokenProvider; +import aiin.backend.util.dto.DataResponse; +import aiin.backend.auth.exception.ApiException; +import aiin.backend.util.responseWriter.ResponseWriter; +import aiin.backend.member.entity.Member; +import aiin.backend.member.service.MemberService; import jakarta.servlet.FilterChain; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -66,10 +66,9 @@ protected void successfulAuthentication( Long memberId = member.getId(); String accessToken = jwtTokenProvider.createAccessToken(memberId); - String refreshToken = jwtTokenProvider.createRefreshToken(); + String refreshToken = jwtTokenProvider.createRefreshToken(memberId); jwtTokenProvider.sendAccessAndRefreshToken(response, accessToken, refreshToken); - jwtTokenProvider.updateRefreshToken(memberId, refreshToken); log.info("로그인 성공: {}", email); log.info("accessToken={}", accessToken); diff --git a/backend/src/main/java/aiin/backend/domains/member/MemberController.java b/backend/src/main/java/aiin/backend/domains/member/MemberController.java deleted file mode 100644 index f276f39..0000000 --- a/backend/src/main/java/aiin/backend/domains/member/MemberController.java +++ /dev/null @@ -1,34 +0,0 @@ -package aiin.backend.domains.member; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import aiin.backend.common.dto.DataResponse; -import aiin.backend.common.security.filter.jwtFilter.JwtTokenProvider; -import aiin.backend.common.util.memberLoader.MemberLoader; -import aiin.backend.domains.member.domain.Member; -import aiin.backend.domains.member.service.MemberService; -import jakarta.servlet.http.HttpServletRequest; -import lombok.RequiredArgsConstructor; - -@RestController -@RequestMapping("/api/members") -@RequiredArgsConstructor -public class MemberController { - - private final MemberService memberService; - private final JwtTokenProvider jwtTokenProvider; - private final MemberLoader memberLoader; - - @PostMapping("/logout") - public ResponseEntity> logoutMember(HttpServletRequest request) { - Member member = memberLoader.getMember(); - String accessToken = jwtTokenProvider.extractAccessToken(request).orElse(null); - - memberService.logoutMember(member, accessToken); - - return ResponseEntity.ok(DataResponse.ok()); - } -} diff --git a/backend/src/main/java/aiin/backend/domains/member/domain/Logout.java b/backend/src/main/java/aiin/backend/domains/member/domain/Logout.java deleted file mode 100644 index b3a96ab..0000000 --- a/backend/src/main/java/aiin/backend/domains/member/domain/Logout.java +++ /dev/null @@ -1,19 +0,0 @@ -package aiin.backend.domains.member.domain; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.data.annotation.Id; -import org.springframework.data.redis.core.RedisHash; -import org.springframework.data.redis.core.index.Indexed; - -@Getter -@AllArgsConstructor -@RedisHash(value = "blacklist", timeToLive = 60 * 60) -public class Logout { - - @Id - private String id; - - @Indexed - private String accessToken; -} diff --git a/backend/src/main/java/aiin/backend/domains/member/domain/Member.java b/backend/src/main/java/aiin/backend/domains/member/domain/Member.java deleted file mode 100644 index db42254..0000000 --- a/backend/src/main/java/aiin/backend/domains/member/domain/Member.java +++ /dev/null @@ -1,43 +0,0 @@ -package aiin.backend.domains.member.domain; - -import static lombok.AccessLevel.*; - -import aiin.backend.common.entity.BaseEntity; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Entity -@Getter -@NoArgsConstructor(access = PROTECTED) -public class Member extends BaseEntity { - - @Id - @Column(name = "member_id") - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(nullable = false, unique = true) - private String email; - - @Column(nullable = false) - private String pw; - - @Enumerated(EnumType.STRING) - @Column(nullable = false) - private MemberRole memberRole; - - @Builder - private Member(String email, String pw, MemberRole memberRole) { - this.email = email; - this.pw = pw; - this.memberRole = memberRole; - } -} diff --git a/backend/src/main/java/aiin/backend/domains/member/repository/LogoutRepository.java b/backend/src/main/java/aiin/backend/domains/member/repository/LogoutRepository.java deleted file mode 100644 index caad09f..0000000 --- a/backend/src/main/java/aiin/backend/domains/member/repository/LogoutRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package aiin.backend.domains.member.repository; - -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Repository; - -import aiin.backend.domains.member.domain.Logout; - -@Repository -public interface LogoutRepository extends CrudRepository { - - boolean existsByAccessToken(String accessToken); -} diff --git a/backend/src/main/java/aiin/backend/domains/member/repository/RefreshTokenRepository.java b/backend/src/main/java/aiin/backend/domains/member/repository/RefreshTokenRepository.java deleted file mode 100644 index b689756..0000000 --- a/backend/src/main/java/aiin/backend/domains/member/repository/RefreshTokenRepository.java +++ /dev/null @@ -1,16 +0,0 @@ -package aiin.backend.domains.member.repository; - -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -import aiin.backend.domains.member.domain.RefreshToken; - -@Repository -public interface RefreshTokenRepository extends CrudRepository { - - Optional findByMemberId(Long memberId); - - void deleteByMemberId(Long MemberId); -} diff --git a/backend/src/main/java/aiin/backend/domains/member/service/LogoutService.java b/backend/src/main/java/aiin/backend/domains/member/service/LogoutService.java deleted file mode 100644 index a8a452b..0000000 --- a/backend/src/main/java/aiin/backend/domains/member/service/LogoutService.java +++ /dev/null @@ -1,24 +0,0 @@ -package aiin.backend.domains.member.service; - -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import aiin.backend.domains.member.domain.Logout; -import aiin.backend.domains.member.repository.LogoutRepository; -import lombok.RequiredArgsConstructor; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class LogoutService { - - private final LogoutRepository logoutRepository; - - public Boolean existsByAccessToken(String token) { - return logoutRepository.existsByAccessToken(token); - } - - public void save(Logout logoutToken) { - logoutRepository.save(logoutToken); - } -} diff --git a/backend/src/main/java/aiin/backend/domains/member/service/MemberService.java b/backend/src/main/java/aiin/backend/domains/member/service/MemberService.java deleted file mode 100644 index 855ffbd..0000000 --- a/backend/src/main/java/aiin/backend/domains/member/service/MemberService.java +++ /dev/null @@ -1,40 +0,0 @@ -package aiin.backend.domains.member.service; - -import aiin.backend.domains.member.domain.Logout; -import aiin.backend.domains.member.domain.Member; -import aiin.backend.domains.member.repository.LogoutRepository; -import aiin.backend.domains.member.repository.MemberRepository; -import aiin.backend.domains.member.repository.RefreshTokenRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Optional; -import java.util.UUID; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class MemberService { - - private final MemberRepository memberRepository; - private final RefreshTokenRepository refreshTokenRepository; - private final LogoutRepository logoutRepository; - - // 이메일로 멤버 조회 - public Optional findByEmail(String email) { - return memberRepository.findByEmail(email); - } - - //로그아웃 - @Transactional - public void logoutMember(Member member, String accessToken) { - Long memberId = member.getId(); - - // 회원의 refreshToken 삭제 - refreshTokenRepository.deleteByMemberId(memberId); - - // 같은 accessToken으로 다시 로그인하지 못하도록 블랙리스트에 저장 - logoutRepository.save(new Logout(UUID.randomUUID().toString(), accessToken)); - } -} diff --git a/backend/src/main/java/aiin/backend/member/controller/MemberController.java b/backend/src/main/java/aiin/backend/member/controller/MemberController.java new file mode 100644 index 0000000..9fd7ab1 --- /dev/null +++ b/backend/src/main/java/aiin/backend/member/controller/MemberController.java @@ -0,0 +1,58 @@ +package aiin.backend.member.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import aiin.backend.member.dto.DeleteRequest; +import aiin.backend.member.dto.SignUpRequest; +import aiin.backend.util.dto.DataResponse; +import aiin.backend.auth.security.jwtFilter.JwtTokenProvider; +import aiin.backend.member.memberLoader.MemberLoader; +import aiin.backend.member.entity.Member; +import aiin.backend.member.service.MemberService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/api/members") +@RequiredArgsConstructor +public class MemberController { + + private final MemberService memberService; + private final JwtTokenProvider jwtTokenProvider; + private final MemberLoader memberLoader; + + @PostMapping("/logout") + public ResponseEntity> logoutMember(HttpServletRequest request) { + Member member = memberLoader.getMember(); + String accessToken = jwtTokenProvider.extractAccessToken(request).orElse(null); + String refreshToken = jwtTokenProvider.extractRefreshToken(request).orElse(null); + + memberService.logoutMember(member, accessToken, refreshToken); + + return ResponseEntity + .ok(DataResponse.ok()); + } + + @PostMapping("/signup") + public ResponseEntity> signupMember(@RequestBody SignUpRequest signUpRequest) { + memberService.signUp(signUpRequest); + + return ResponseEntity + .status(HttpStatus.CREATED) + .body(DataResponse.created()); + } + + @PostMapping("/delete") + public ResponseEntity> deleteMember(@RequestBody DeleteRequest deleteRequest) { + memberService.deleteMember(deleteRequest); + + return ResponseEntity + .status(HttpStatus.CREATED) + .body(DataResponse.ok()); + } +} diff --git a/backend/src/main/java/aiin/backend/member/dto/DeleteRequest.java b/backend/src/main/java/aiin/backend/member/dto/DeleteRequest.java new file mode 100644 index 0000000..0c023a1 --- /dev/null +++ b/backend/src/main/java/aiin/backend/member/dto/DeleteRequest.java @@ -0,0 +1,7 @@ +package aiin.backend.member.dto; + +public record DeleteRequest( + String email +) +{ +} diff --git a/backend/src/main/java/aiin/backend/member/dto/SignUpRequest.java b/backend/src/main/java/aiin/backend/member/dto/SignUpRequest.java new file mode 100644 index 0000000..4fed225 --- /dev/null +++ b/backend/src/main/java/aiin/backend/member/dto/SignUpRequest.java @@ -0,0 +1,17 @@ +package aiin.backend.member.dto; + +import java.time.LocalDateTime; + +import aiin.backend.member.model.Gender; +import aiin.backend.member.model.MemberRole; +import aiin.backend.member.model.Provider; + +public record SignUpRequest( + String username, + String email, + String password, + Gender gender, + String phoneNumber, + LocalDateTime birth) +{ +} diff --git a/backend/src/main/java/aiin/backend/member/entity/Member.java b/backend/src/main/java/aiin/backend/member/entity/Member.java new file mode 100644 index 0000000..bd8846e --- /dev/null +++ b/backend/src/main/java/aiin/backend/member/entity/Member.java @@ -0,0 +1,80 @@ +package aiin.backend.member.entity; + +import static lombok.AccessLevel.*; + +import java.time.LocalDateTime; + +import aiin.backend.member.model.Gender; +import aiin.backend.member.model.MemberRole; +import aiin.backend.member.model.Provider; +import aiin.backend.util.entity.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Table(schema = "members") +@NoArgsConstructor(access = PROTECTED) +public class Member extends BaseEntity { + + @Id + @Column(name = "member_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false, unique = true) + private String username; + + @Column(nullable = false, unique = true) + private String email; + + @Column(nullable = false) + private String password; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private MemberRole role; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private Gender gender; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private Provider provider; + + @Column(nullable = true) + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "profile_image_id", referencedColumnName = "profile_id") + ProfileImage profileImage; + + @Column(nullable = false, name = "phone_number") + private String phoneNumber; + + @Column(nullable = false, name = "birth_date") + private LocalDateTime birthDate; + + @Builder + private Member(String username, String email, String password, MemberRole memberRole, Gender gender, Provider provider, String phoneNumber, LocalDateTime birthDate) { + this.username = username; + this.email = email; + this.password = password; + this.role = memberRole; + this.phoneNumber = phoneNumber; + this.birthDate = birthDate; + this.gender = gender; + this.provider = provider; + } +} diff --git a/backend/src/main/java/aiin/backend/member/entity/ProfileImage.java b/backend/src/main/java/aiin/backend/member/entity/ProfileImage.java new file mode 100644 index 0000000..ba2dba0 --- /dev/null +++ b/backend/src/main/java/aiin/backend/member/entity/ProfileImage.java @@ -0,0 +1,32 @@ +package aiin.backend.member.entity; + +import aiin.backend.util.entity.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; + +@Entity +@Getter +@Table(schema = "profile_images") +public class ProfileImage extends BaseEntity { + @Id + @Column(name = "profile_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private String id; + + @Column(nullable = true) + private String image_file_url; + + @Column(nullable = true) + private Long image_file_size; + + @Column(nullable = true) + private String image_file_name; + + @Column(nullable = true) + private String image_file_extension; +} diff --git a/backend/src/main/java/aiin/backend/domains/member/domain/RefreshToken.java b/backend/src/main/java/aiin/backend/member/entity/RefreshToken.java similarity index 80% rename from backend/src/main/java/aiin/backend/domains/member/domain/RefreshToken.java rename to backend/src/main/java/aiin/backend/member/entity/RefreshToken.java index 36020ec..49abee7 100644 --- a/backend/src/main/java/aiin/backend/domains/member/domain/RefreshToken.java +++ b/backend/src/main/java/aiin/backend/member/entity/RefreshToken.java @@ -1,4 +1,4 @@ -package aiin.backend.domains.member.domain; +package aiin.backend.member.entity; import lombok.AllArgsConstructor; import lombok.Getter; @@ -14,8 +14,8 @@ public class RefreshToken { @Id @Setter - private String token; + private String accessToken; @Indexed - private Long memberId; + private String refreshToken; } diff --git a/backend/src/main/java/aiin/backend/common/util/memberLoader/MemberLoader.java b/backend/src/main/java/aiin/backend/member/memberLoader/MemberLoader.java similarity index 74% rename from backend/src/main/java/aiin/backend/common/util/memberLoader/MemberLoader.java rename to backend/src/main/java/aiin/backend/member/memberLoader/MemberLoader.java index 13ea884..3246e65 100644 --- a/backend/src/main/java/aiin/backend/common/util/memberLoader/MemberLoader.java +++ b/backend/src/main/java/aiin/backend/member/memberLoader/MemberLoader.java @@ -1,15 +1,15 @@ -package aiin.backend.common.util.memberLoader; +package aiin.backend.member.memberLoader; -import static aiin.backend.common.exception.ErrorCode.*; +import static aiin.backend.auth.exception.ErrorCode.*; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import aiin.backend.common.exception.ApiException; -import aiin.backend.domains.member.domain.Member; -import aiin.backend.domains.member.repository.MemberRepository; +import aiin.backend.auth.exception.ApiException; +import aiin.backend.member.entity.Member; +import aiin.backend.member.repository.MemberRepository; import lombok.RequiredArgsConstructor; @Component diff --git a/backend/src/main/java/aiin/backend/member/model/Gender.java b/backend/src/main/java/aiin/backend/member/model/Gender.java new file mode 100644 index 0000000..0eb94cc --- /dev/null +++ b/backend/src/main/java/aiin/backend/member/model/Gender.java @@ -0,0 +1,10 @@ +package aiin.backend.member.model; + +public enum Gender { + MALE("male"), + FEMALE("female"); + + private final String value; + + Gender(String value) { this.value = value; } +} diff --git a/backend/src/main/java/aiin/backend/domains/member/domain/MemberRole.java b/backend/src/main/java/aiin/backend/member/model/MemberRole.java similarity index 79% rename from backend/src/main/java/aiin/backend/domains/member/domain/MemberRole.java rename to backend/src/main/java/aiin/backend/member/model/MemberRole.java index 068fdff..80c599f 100644 --- a/backend/src/main/java/aiin/backend/domains/member/domain/MemberRole.java +++ b/backend/src/main/java/aiin/backend/member/model/MemberRole.java @@ -1,4 +1,4 @@ -package aiin.backend.domains.member.domain; +package aiin.backend.member.model; import lombok.Getter; diff --git a/backend/src/main/java/aiin/backend/member/model/Provider.java b/backend/src/main/java/aiin/backend/member/model/Provider.java new file mode 100644 index 0000000..3084566 --- /dev/null +++ b/backend/src/main/java/aiin/backend/member/model/Provider.java @@ -0,0 +1,11 @@ +package aiin.backend.member.model; + +public enum Provider { + KAKAO("kakao"), + AIMO("aimo"); + + private final String value; + Provider(String value) { + this.value = value; + } +} diff --git a/backend/src/main/java/aiin/backend/domains/member/repository/MemberRepository.java b/backend/src/main/java/aiin/backend/member/repository/MemberRepository.java similarity index 62% rename from backend/src/main/java/aiin/backend/domains/member/repository/MemberRepository.java rename to backend/src/main/java/aiin/backend/member/repository/MemberRepository.java index 720a998..dc9ead5 100644 --- a/backend/src/main/java/aiin/backend/domains/member/repository/MemberRepository.java +++ b/backend/src/main/java/aiin/backend/member/repository/MemberRepository.java @@ -1,10 +1,12 @@ -package aiin.backend.domains.member.repository; +package aiin.backend.member.repository; + +import aiin.backend.member.entity.Member; -import aiin.backend.domains.member.domain.Member; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; public interface MemberRepository extends JpaRepository { Optional findByEmail(String email); + void deleteMemberByEmail(String email); } diff --git a/backend/src/main/java/aiin/backend/member/repository/RefreshTokenRepository.java b/backend/src/main/java/aiin/backend/member/repository/RefreshTokenRepository.java new file mode 100644 index 0000000..dc4381f --- /dev/null +++ b/backend/src/main/java/aiin/backend/member/repository/RefreshTokenRepository.java @@ -0,0 +1,16 @@ +package aiin.backend.member.repository; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +import aiin.backend.member.entity.RefreshToken; + +@Repository +public interface RefreshTokenRepository extends CrudRepository { + + Optional findByAccessToken(String accessToken); + boolean existsByAccessToken(String accessToken); + void deleteByAccessToken(String accessToken); +} diff --git a/backend/src/main/java/aiin/backend/member/service/LogoutService.java b/backend/src/main/java/aiin/backend/member/service/LogoutService.java new file mode 100644 index 0000000..631cd38 --- /dev/null +++ b/backend/src/main/java/aiin/backend/member/service/LogoutService.java @@ -0,0 +1,19 @@ +package aiin.backend.member.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import aiin.backend.member.repository.RefreshTokenRepository; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class LogoutService { + + private final RefreshTokenRepository refreshTokenRepository; + + public Boolean existsByAccessToken(String accessToken) { + return refreshTokenRepository.existsByAccessToken(accessToken); + } +} diff --git a/backend/src/main/java/aiin/backend/member/service/MemberService.java b/backend/src/main/java/aiin/backend/member/service/MemberService.java new file mode 100644 index 0000000..6ca45e0 --- /dev/null +++ b/backend/src/main/java/aiin/backend/member/service/MemberService.java @@ -0,0 +1,54 @@ +package aiin.backend.member.service; + +import aiin.backend.auth.exception.ApiException; +import aiin.backend.auth.exception.ErrorCode; +import aiin.backend.member.dto.DeleteRequest; +import aiin.backend.member.dto.SignUpRequest; +import aiin.backend.member.entity.Member; +import aiin.backend.member.entity.RefreshToken; +import aiin.backend.member.mapper.MemberMapper; +import aiin.backend.member.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class MemberService { + + private final MemberRepository memberRepository; + private final RefreshTokenService refreshTokenService; + + // 이메일로 멤버 조회 + public Optional findByEmail(String email) { + return memberRepository.findByEmail(email); + } + + // 회원 가입 + @Transactional + public void signUp(SignUpRequest signUpRequest) { + Member member = MemberMapper.signUpMemberEntity(signUpRequest); + memberRepository.save(member); + } + + + //로그아웃 + @Transactional + public void logoutMember(Member member, String accessToken, String refreshToken) { + // 회원의 refreshToken 만료 처리 + RefreshToken expiredToken = new RefreshToken(accessToken, refreshToken); + refreshTokenService.save(expiredToken); + } + + @Transactional + public void deleteMember(DeleteRequest deleteRequest) { + Member member = memberRepository + .findByEmail(deleteRequest.email()) + .orElseThrow(()-> ApiException.from(ErrorCode.MEMBER_NOT_FOUND)); + + memberRepository.delete(member); + } +} diff --git a/backend/src/main/java/aiin/backend/domains/member/service/RefreshTokenService.java b/backend/src/main/java/aiin/backend/member/service/RefreshTokenService.java similarity index 52% rename from backend/src/main/java/aiin/backend/domains/member/service/RefreshTokenService.java rename to backend/src/main/java/aiin/backend/member/service/RefreshTokenService.java index c303a10..8973bbd 100644 --- a/backend/src/main/java/aiin/backend/domains/member/service/RefreshTokenService.java +++ b/backend/src/main/java/aiin/backend/member/service/RefreshTokenService.java @@ -1,12 +1,12 @@ -package aiin.backend.domains.member.service; +package aiin.backend.member.service; import java.util.Optional; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import aiin.backend.domains.member.domain.RefreshToken; -import aiin.backend.domains.member.repository.RefreshTokenRepository; +import aiin.backend.member.entity.RefreshToken; +import aiin.backend.member.repository.RefreshTokenRepository; import lombok.RequiredArgsConstructor; @Service @@ -16,19 +16,20 @@ public class RefreshTokenService { private final RefreshTokenRepository refreshTokenRepository; - public Optional findByMemberId(Long memberId) { - return refreshTokenRepository.findByMemberId(memberId); + public Optional findByAccessToken(String accessToken) { + return refreshTokenRepository.findByAccessToken(accessToken); } public void save(RefreshToken refreshToken) { refreshTokenRepository.save(refreshToken); } - public Optional findByToken(String token) { return refreshTokenRepository.findById(token); } - public void deleteByMemberId(Long memberId) { - refreshTokenRepository.deleteByMemberId(memberId); + public void deleteByAccessToken(String accessToken) { + refreshTokenRepository.deleteByAccessToken(accessToken); } + + public Boolean existsByAccessToken(String accessToken) { return refreshTokenRepository.existsByAccessToken(accessToken); } } diff --git a/backend/src/main/java/aiin/backend/common/dto/BaseResponse.java b/backend/src/main/java/aiin/backend/util/dto/BaseResponse.java similarity index 93% rename from backend/src/main/java/aiin/backend/common/dto/BaseResponse.java rename to backend/src/main/java/aiin/backend/util/dto/BaseResponse.java index 550ffd0..3192478 100644 --- a/backend/src/main/java/aiin/backend/common/dto/BaseResponse.java +++ b/backend/src/main/java/aiin/backend/util/dto/BaseResponse.java @@ -1,4 +1,4 @@ -package aiin.backend.common.dto; +package aiin.backend.util.dto; import com.fasterxml.jackson.annotation.JsonFormat; diff --git a/backend/src/main/java/aiin/backend/common/dto/DataResponse.java b/backend/src/main/java/aiin/backend/util/dto/DataResponse.java similarity index 69% rename from backend/src/main/java/aiin/backend/common/dto/DataResponse.java rename to backend/src/main/java/aiin/backend/util/dto/DataResponse.java index fe436ef..18a0970 100644 --- a/backend/src/main/java/aiin/backend/common/dto/DataResponse.java +++ b/backend/src/main/java/aiin/backend/util/dto/DataResponse.java @@ -1,4 +1,4 @@ -package aiin.backend.common.dto; +package aiin.backend.util.dto; import org.springframework.http.HttpStatus; @@ -24,4 +24,6 @@ public static DataResponse from(T data) { public static DataResponse ok() { return new DataResponse<>(HttpStatus.OK, null); } + public static DataResponse created() { return new DataResponse<>(HttpStatus.CREATED, null); } + public static DataResponse delete() { return new DataResponse<>(HttpStatus.NO_CONTENT, null); } } diff --git a/backend/src/main/java/aiin/backend/common/dto/ErrorResponse.java b/backend/src/main/java/aiin/backend/util/dto/ErrorResponse.java similarity index 93% rename from backend/src/main/java/aiin/backend/common/dto/ErrorResponse.java rename to backend/src/main/java/aiin/backend/util/dto/ErrorResponse.java index 2ec8ea8..9765b72 100644 --- a/backend/src/main/java/aiin/backend/common/dto/ErrorResponse.java +++ b/backend/src/main/java/aiin/backend/util/dto/ErrorResponse.java @@ -1,4 +1,4 @@ -package aiin.backend.common.dto; +package aiin.backend.util.dto; import java.util.List; @@ -6,7 +6,7 @@ import com.fasterxml.jackson.annotation.JsonInclude; -import aiin.backend.common.exception.ErrorCode; +import aiin.backend.auth.exception.ErrorCode; import lombok.Getter; @Getter diff --git a/backend/src/main/java/aiin/backend/common/util/encoder/PasswordEncoderConfig.java b/backend/src/main/java/aiin/backend/util/encoder/PasswordEncoderConfig.java similarity index 91% rename from backend/src/main/java/aiin/backend/common/util/encoder/PasswordEncoderConfig.java rename to backend/src/main/java/aiin/backend/util/encoder/PasswordEncoderConfig.java index 1c984e0..e273e8a 100644 --- a/backend/src/main/java/aiin/backend/common/util/encoder/PasswordEncoderConfig.java +++ b/backend/src/main/java/aiin/backend/util/encoder/PasswordEncoderConfig.java @@ -1,4 +1,4 @@ -package aiin.backend.common.util.encoder; +package aiin.backend.util.encoder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/backend/src/main/java/aiin/backend/common/util/encoder/PasswordEncoderUtil.java b/backend/src/main/java/aiin/backend/util/encoder/PasswordEncoderUtil.java similarity index 90% rename from backend/src/main/java/aiin/backend/common/util/encoder/PasswordEncoderUtil.java rename to backend/src/main/java/aiin/backend/util/encoder/PasswordEncoderUtil.java index d866bfd..b1e042b 100644 --- a/backend/src/main/java/aiin/backend/common/util/encoder/PasswordEncoderUtil.java +++ b/backend/src/main/java/aiin/backend/util/encoder/PasswordEncoderUtil.java @@ -1,4 +1,4 @@ -package aiin.backend.common.util.encoder; +package aiin.backend.util.encoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; diff --git a/backend/src/main/java/aiin/backend/common/entity/BaseEntity.java b/backend/src/main/java/aiin/backend/util/entity/BaseEntity.java similarity index 94% rename from backend/src/main/java/aiin/backend/common/entity/BaseEntity.java rename to backend/src/main/java/aiin/backend/util/entity/BaseEntity.java index 2bff6a5..44b7532 100644 --- a/backend/src/main/java/aiin/backend/common/entity/BaseEntity.java +++ b/backend/src/main/java/aiin/backend/util/entity/BaseEntity.java @@ -1,4 +1,4 @@ -package aiin.backend.common.entity; +package aiin.backend.util.entity; import jakarta.persistence.Column; import jakarta.persistence.EntityListeners; diff --git a/backend/src/main/java/aiin/backend/common/util/responseWriter/ResponseWriter.java b/backend/src/main/java/aiin/backend/util/responseWriter/ResponseWriter.java similarity index 91% rename from backend/src/main/java/aiin/backend/common/util/responseWriter/ResponseWriter.java rename to backend/src/main/java/aiin/backend/util/responseWriter/ResponseWriter.java index 270d12f..f6b5a35 100644 --- a/backend/src/main/java/aiin/backend/common/util/responseWriter/ResponseWriter.java +++ b/backend/src/main/java/aiin/backend/util/responseWriter/ResponseWriter.java @@ -1,4 +1,4 @@ -package aiin.backend.common.util.responseWriter; +package aiin.backend.util.responseWriter; import java.io.IOException; @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import aiin.backend.common.dto.BaseResponse; +import aiin.backend.util.dto.BaseResponse; import jakarta.servlet.http.HttpServletResponse; public class ResponseWriter { From e3ed58ebc1b5fd662a6f72078106db95132bac3b Mon Sep 17 00:00:00 2001 From: mng990 Date: Tue, 22 Oct 2024 02:14:37 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=201.=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EA=B5=AC=ED=98=84=202.=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=EA=B5=AC=ED=98=84=203.=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EA=B5=AC=EC=A1=B0=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 --- .../backend/member/mapper/MemberMapper.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 backend/src/main/java/aiin/backend/member/mapper/MemberMapper.java diff --git a/backend/src/main/java/aiin/backend/member/mapper/MemberMapper.java b/backend/src/main/java/aiin/backend/member/mapper/MemberMapper.java new file mode 100644 index 0000000..9bc7910 --- /dev/null +++ b/backend/src/main/java/aiin/backend/member/mapper/MemberMapper.java @@ -0,0 +1,27 @@ +package aiin.backend.member.mapper; + +import org.springframework.stereotype.Component; + +import aiin.backend.member.dto.SignUpRequest; +import aiin.backend.member.entity.Member; +import aiin.backend.member.model.MemberRole; +import aiin.backend.member.model.Provider; + + +@Component +public class MemberMapper { + + public static Member signUpMemberEntity(SignUpRequest signUpRequest){ + return Member + .builder() + .username(signUpRequest.username()) + .password(signUpRequest.password()) + .email(signUpRequest.email()) + .memberRole(MemberRole.USER) + .gender(signUpRequest.gender()) + .provider(Provider.AIMO) + .phoneNumber(signUpRequest.phoneNumber()) + .birthDate(signUpRequest.birth()) + .build(); + } +}