From 0d084fa91439e68a63d98ce88355a5c1aa6397e6 Mon Sep 17 00:00:00 2001 From: Park Seyeon Date: Mon, 13 Nov 2023 16:51:17 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=20annotation=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=A0=9C=EA=B3=B5=20(#62)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 회원 엔티티 생성 및 테스트코드 추가 * feat: 카카오 OAuth 환경변수 추가 및 클래스 바인딩 * feat: authorization code를 받기 위한 queryString generator 추가 * feat: Authorization code의 parameter 만드는 로직 분리 및 테스트 코드 추가 * feat: 회원 가입/로그인 요청 api 및 소셜 로그인 페이지 반환 * refactor: member관련 클래스 네이밍과 폴더 위치 변경 * refactor: 로그인 페이지 요청 방식 Resttemplate -> response (redirect)하도록 변경 * style: 코드 포맷 재적용 및 사용하지 않는 클래스 삭제 * chore: config 파일 업데이트 * refactor: 테스트 코드 추가 및 코드 포맷 재적용 * refactor: 사용하지 않는 코드 제거 * refactor: CRLF -> LF로 변경 * fix: config 커밋, config 최근 커밋으로 변경 * feat: 테스트 코드 추가 및 패키지 구조 변경 * refactor: revert merge * fix: merge confilt해결 및 예외처리 추가 * test: oauth properties가 없을 때의 테스트코드 추가 * feat: 코드리뷰에 따른 기능 분리 및 테스트 코드 변경 * fix: 테스트코드 관련 code smell 제거 * feat: Authorization grant 받기 예외 코드 및 테스트 코드 추가 * feat: Authorization Token 요청 및 반환 코드, 에러 반환 테스트 코드 추가 * refactor: AuthenticationService에서 서버에 요청보내는 로직 OAuth2AuthorizationServerRequestService로 분리 * test: 로그인 요청 테스트 코드 추가 * feat: 토큰 발급 요청 기능 테스트 코드 추가 및 RestTemplate 필드변수로 변경 * test: restTemplate 및 서비스 테스트 추가 * refactor: 에러 메세지 이름 변경 * refacotr: 변수명 및 entity default 명 변경 * feat: 토큰 정보 조회 기능 및 테스트 추가 * feat: 사용자 토큰 정보 조회 및 테스트 코드 & Resttemplate 테크트 코드 변경 * fix: encoding, formatting, tab 문제로 인한 파일 삭제 후 다시 작성 * feat: JWT 토큰 제공 서비스 및 테스트 코드 추가 * feat: 토큰 인증 코드 및 테스트 코드 작성 * feat: 로그인 및 회원가입 기능 추가 - 회원의 socialId string -> long으로 변경 * feat: 회원 로그인 테스트 코드 추가 * chore: 코드 포메팅 재 설정 * feat: config 파일 업데이트 * feat: Window용 포트 redis 포트 변경 추가 * refacotr: develop 업데이트 사항 merge * refactor: develop 업데이트 부분 merge * fix: TimeConfig 삭제 및 코드 스멜 변경 * refactor: 코르리뷰 반영 * chore: submodule update * feat: 메서드 파싱 customizing 및 @CurrentMember AuthorizationMember 를 파라미터로 감지하는 조건 추가 * feat: 인가회원에 대한 객체 ThreadLocalMap에 저장하는 기능 추가 * fix: 회원 정보 Optional 정보 조회 버그 fix, socialId requiredNotNull추가 등 에러 수정 * feat: API요청 Path 및 인증에 따른 filter 추가 - PathFilter: PathResolver, WebConfig - AuthorizationFilter:AuthorizationService, JwtAuthenticationService, JwtProviderService, MemberService - Member info: CurrentMember, AuthorizationMember, LoginResponse, MemberMapper, CurrentMember, PublicClaim, CurrentMemberArgumentResolver * test: CurrentMember 테스트 support 추가 * test: authorizationfilter 및 pathfilter 테스트 추가 * test: 회원 repostiory 및 fixture 추가 * test: filter support 클랠스 추가 * test: filter support 클래스 적용 * refactor: PublicClaim 변환 책임 변경 * test: PathResolver, CurrentMemberArgumentResovler테스트 코드 추가 * fix: 모든 쿠키 secure 적용되도록 변경 * refactor: 클래스 명 변경 * refactor: webConfig Path 매핑 클래스 추가 --- ...Service.java => AuthorizationService.java} | 27 +- .../application/JwtAuthenticationService.java | 16 +- .../api/application/JwtProviderService.java | 28 +- .../moabam/api/application/MemberService.java | 6 +- .../com/moabam/api/domain/entity/Member.java | 2 +- ...thMapper.java => AuthorizationMapper.java} | 17 +- .../moabam/api/dto/AuthorizationMember.java | 11 + .../com/moabam/api/dto/LoginResponse.java | 6 +- .../java/com/moabam/api/dto/MemberMapper.java | 14 +- .../java/com/moabam/api/dto/PathMapper.java | 43 + .../java/com/moabam/api/dto/PublicClaim.java | 15 + .../api/presentation/MemberController.java | 12 +- .../common/annotation/CurrentMember.java | 12 + .../CurrentMemberArgumentResolver.java | 31 + .../global/common/handler/PathResolver.java | 85 + .../common/util/AuthorizationThreadLocal.java | 28 + .../global/common/util/CookieUtils.java | 11 +- .../com/moabam/global/config/WebConfig.java | 30 + .../global/error/model/ErrorMessage.java | 2 +- .../global/filter/AuthorizationFilter.java | 113 + .../com/moabam/global/filter/PathFilter.java | 39 + src/main/resources/static/docs/coupon.html | 2791 +++++++++++++---- src/main/resources/static/docs/index.html | 2 +- .../resources/static/docs/notification.html | 2605 ++++++++++++--- ...est.java => AuthorizationServiceTest.java} | 51 +- .../JwtAuthenticationServiceTest.java | 47 +- .../application/JwtProviderServiceTest.java | 13 +- .../api/application/MemberServiceTest.java | 4 +- .../repository/MemberRepositoryTest.java | 32 + .../api/presentation/BugControllerTest.java | 5 +- .../presentation/CouponControllerTest.java | 3 +- .../api/presentation/ItemControllerTest.java | 23 +- .../presentation/MemberControllerTest.java | 8 +- .../NotificationControllerTest.java | 3 +- .../presentation/ProductControllerTest.java | 5 +- .../api/presentation/RoomControllerTest.java | 5 +- .../CurrentMemberArgumentResolverTest.java | 115 + .../common/handler/PathResolverTest.java | 65 + .../filter/AuthorizationFilterTest.java | 174 + .../moabam/global/filter/PathFilterTest.java | 76 + .../moabam/support/annotation/WithMember.java | 19 + .../common/FilterProcessExtension.java | 61 + .../support/common/RestDocsFactory.java | 31 + .../support/common/WithFilterSupporter.java | 51 + .../common/WithoutFilterSupporter.java | 37 + .../support/fixture/JwtProviderFixture.java | 19 + .../support/fixture/PublicClaimFixture.java | 15 + src/test/resources/application.yml | 1 + 48 files changed, 5682 insertions(+), 1127 deletions(-) rename src/main/java/com/moabam/api/application/{AuthenticationService.java => AuthorizationService.java} (81%) rename src/main/java/com/moabam/api/dto/{OAuthMapper.java => AuthorizationMapper.java} (60%) create mode 100644 src/main/java/com/moabam/api/dto/AuthorizationMember.java create mode 100644 src/main/java/com/moabam/api/dto/PathMapper.java create mode 100644 src/main/java/com/moabam/api/dto/PublicClaim.java create mode 100644 src/main/java/com/moabam/global/common/annotation/CurrentMember.java create mode 100644 src/main/java/com/moabam/global/common/handler/CurrentMemberArgumentResolver.java create mode 100644 src/main/java/com/moabam/global/common/handler/PathResolver.java create mode 100644 src/main/java/com/moabam/global/common/util/AuthorizationThreadLocal.java create mode 100644 src/main/java/com/moabam/global/filter/AuthorizationFilter.java create mode 100644 src/main/java/com/moabam/global/filter/PathFilter.java rename src/test/java/com/moabam/api/application/{AuthenticationServiceTest.java => AuthorizationServiceTest.java} (77%) create mode 100644 src/test/java/com/moabam/api/domain/repository/MemberRepositoryTest.java create mode 100644 src/test/java/com/moabam/global/common/handler/CurrentMemberArgumentResolverTest.java create mode 100644 src/test/java/com/moabam/global/common/handler/PathResolverTest.java create mode 100644 src/test/java/com/moabam/global/filter/AuthorizationFilterTest.java create mode 100644 src/test/java/com/moabam/global/filter/PathFilterTest.java create mode 100644 src/test/java/com/moabam/support/annotation/WithMember.java create mode 100644 src/test/java/com/moabam/support/common/FilterProcessExtension.java create mode 100644 src/test/java/com/moabam/support/common/RestDocsFactory.java create mode 100644 src/test/java/com/moabam/support/common/WithFilterSupporter.java create mode 100644 src/test/java/com/moabam/support/common/WithoutFilterSupporter.java create mode 100644 src/test/java/com/moabam/support/fixture/JwtProviderFixture.java create mode 100644 src/test/java/com/moabam/support/fixture/PublicClaimFixture.java diff --git a/src/main/java/com/moabam/api/application/AuthenticationService.java b/src/main/java/com/moabam/api/application/AuthorizationService.java similarity index 81% rename from src/main/java/com/moabam/api/application/AuthenticationService.java rename to src/main/java/com/moabam/api/application/AuthorizationService.java index 384d4caa..8f2db0e3 100644 --- a/src/main/java/com/moabam/api/application/AuthenticationService.java +++ b/src/main/java/com/moabam/api/application/AuthorizationService.java @@ -9,14 +9,16 @@ import com.moabam.api.dto.AuthorizationCodeRequest; import com.moabam.api.dto.AuthorizationCodeResponse; +import com.moabam.api.dto.AuthorizationMapper; import com.moabam.api.dto.AuthorizationTokenInfoResponse; import com.moabam.api.dto.AuthorizationTokenRequest; import com.moabam.api.dto.AuthorizationTokenResponse; import com.moabam.api.dto.LoginResponse; -import com.moabam.api.dto.OAuthMapper; +import com.moabam.api.dto.PublicClaim; import com.moabam.global.common.util.CookieUtils; import com.moabam.global.common.util.GlobalConstant; import com.moabam.global.config.OAuthConfig; +import com.moabam.global.config.TokenConfig; import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.model.ErrorMessage; @@ -25,9 +27,10 @@ @Service @RequiredArgsConstructor -public class AuthenticationService { +public class AuthorizationService { private final OAuthConfig oAuthConfig; + private final TokenConfig tokenConfig; private final OAuth2AuthorizationServerRequestService oauth2AuthorizationServerRequestService; private final MemberService memberService; private final JwtProviderService jwtProviderService; @@ -55,13 +58,13 @@ public AuthorizationTokenInfoResponse requestTokenInfo(AuthorizationTokenRespons public LoginResponse signUpOrLogin(HttpServletResponse httpServletResponse, AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { LoginResponse loginResponse = memberService.login(authorizationTokenInfoResponse); - issueServiceToken(httpServletResponse, loginResponse.id()); + issueServiceToken(httpServletResponse, loginResponse.publicClaim()); return loginResponse; } private String getAuthorizationCodeUri() { - AuthorizationCodeRequest authorizationCodeRequest = OAuthMapper.toAuthorizationCodeRequest(oAuthConfig); + AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oAuthConfig); return generateQueryParamsWith(authorizationCodeRequest); } @@ -91,7 +94,8 @@ private void validAuthorizationGrant(String code) { } private AuthorizationTokenResponse issueTokenToAuthorizationServer(String code) { - AuthorizationTokenRequest authorizationTokenRequest = OAuthMapper.toAuthorizationTokenRequest(oAuthConfig, + AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( + oAuthConfig, code); MultiValueMap uriParams = generateTokenRequest(authorizationTokenRequest); ResponseEntity authorizationTokenResponse = @@ -115,9 +119,14 @@ private MultiValueMap generateTokenRequest(AuthorizationTokenReq return contents; } - private void issueServiceToken(HttpServletResponse response, Long id) { - response.addHeader("token_type", "Bearer"); - response.addCookie(CookieUtils.tokenCookie("access_token", jwtProviderService.provideAccessToken(id))); - response.addCookie(CookieUtils.tokenCookie("refresh_token", jwtProviderService.provideRefreshToken(id))); + public void issueServiceToken(HttpServletResponse response, PublicClaim publicClaim) { + response.addCookie( + CookieUtils.typeCookie("Bearer", tokenConfig.getRefreshExpire())); + response.addCookie( + CookieUtils.tokenCookie("access_token", jwtProviderService.provideAccessToken(publicClaim), + tokenConfig.getRefreshExpire())); + response.addCookie( + CookieUtils.tokenCookie("refresh_token", jwtProviderService.provideRefreshToken(), + tokenConfig.getRefreshExpire())); } } diff --git a/src/main/java/com/moabam/api/application/JwtAuthenticationService.java b/src/main/java/com/moabam/api/application/JwtAuthenticationService.java index 14247dab..4956e397 100644 --- a/src/main/java/com/moabam/api/application/JwtAuthenticationService.java +++ b/src/main/java/com/moabam/api/application/JwtAuthenticationService.java @@ -5,6 +5,8 @@ import org.json.JSONObject; import org.springframework.stereotype.Service; +import com.moabam.api.dto.AuthorizationMapper; +import com.moabam.api.dto.PublicClaim; import com.moabam.global.config.TokenConfig; import com.moabam.global.error.exception.UnauthorizedException; import com.moabam.global.error.model.ErrorMessage; @@ -19,25 +21,25 @@ public class JwtAuthenticationService { private final TokenConfig tokenConfig; - public boolean isTokenValid(String token) { + public boolean isTokenExpire(String token) { try { Jwts.parserBuilder() .setSigningKey(tokenConfig.getKey()) .build() - .parseClaimsJwt(token); - return true; - } catch (ExpiredJwtException expiredJwtException) { + .parseClaimsJws(token); return false; + } catch (ExpiredJwtException expiredJwtException) { + return true; } catch (Exception exception) { - throw new UnauthorizedException(ErrorMessage.AUTHENTICATIE_FAIL); + throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); } } - public String parseEmail(String token) { + public PublicClaim parseClaim(String token) { String claims = token.split("\\.")[1]; String decodeClaims = new String(Base64.getDecoder().decode(claims)); JSONObject jsonObject = new JSONObject(decodeClaims); - return (String)jsonObject.get("id"); + return AuthorizationMapper.toPublicClaim(jsonObject); } } diff --git a/src/main/java/com/moabam/api/application/JwtProviderService.java b/src/main/java/com/moabam/api/application/JwtProviderService.java index 50466841..1ea2735b 100644 --- a/src/main/java/com/moabam/api/application/JwtProviderService.java +++ b/src/main/java/com/moabam/api/application/JwtProviderService.java @@ -4,8 +4,10 @@ import org.springframework.stereotype.Service; +import com.moabam.api.dto.PublicClaim; import com.moabam.global.config.TokenConfig; +import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import lombok.RequiredArgsConstructor; @@ -16,15 +18,27 @@ public class JwtProviderService { private final TokenConfig tokenConfig; - public String provideAccessToken(long id) { - return generateToken(id, tokenConfig.getAccessExpire()); + public String provideAccessToken(PublicClaim publicClaim) { + return generateIdToken(publicClaim, tokenConfig.getAccessExpire()); } - public String provideRefreshToken(long id) { - return generateToken(id, tokenConfig.getRefreshExpire()); + public String provideRefreshToken() { + return generateCommonInfo(tokenConfig.getRefreshExpire()); } - private String generateToken(long id, long expireTime) { + private String generateIdToken(PublicClaim publicClaim, long expireTime) { + return commonInfo(expireTime) + .claim("id", publicClaim.id()) + .claim("nickname", publicClaim.nickname()) + .claim("role", publicClaim.role()) + .compact(); + } + + private String generateCommonInfo(long expireTime) { + return commonInfo(expireTime).compact(); + } + + private JwtBuilder commonInfo(long expireTime) { Date issueDate = new Date(); Date expireDate = new Date(issueDate.getTime() + expireTime); @@ -34,8 +48,6 @@ private String generateToken(long id, long expireTime) { .setIssuer(tokenConfig.getIss()) .setIssuedAt(issueDate) .setExpiration(expireDate) - .claim("id", id) - .signWith(tokenConfig.getKey(), SignatureAlgorithm.HS256) - .compact(); + .signWith(tokenConfig.getKey(), SignatureAlgorithm.HS256); } } diff --git a/src/main/java/com/moabam/api/application/MemberService.java b/src/main/java/com/moabam/api/application/MemberService.java index f3609251..ca37cdea 100644 --- a/src/main/java/com/moabam/api/application/MemberService.java +++ b/src/main/java/com/moabam/api/application/MemberService.java @@ -13,7 +13,6 @@ import com.moabam.api.domain.entity.Member; import com.moabam.api.domain.repository.MemberRepository; import com.moabam.api.domain.repository.MemberSearchRepository; -import com.moabam.api.domain.repository.NotificationRepository; import com.moabam.api.dto.AuthorizationTokenInfoResponse; import com.moabam.api.dto.LoginResponse; import com.moabam.api.dto.MemberMapper; @@ -28,7 +27,6 @@ public class MemberService { private final MemberRepository memberRepository; private final MemberSearchRepository memberSearchRepository; - private final NotificationRepository notificationRepository; public Member getById(Long memberId) { return memberRepository.findById(memberId) @@ -38,9 +36,9 @@ public Member getById(Long memberId) { @Transactional public LoginResponse login(AuthorizationTokenInfoResponse authorizationTokenInfoResponse) { Optional member = memberRepository.findBySocialId(authorizationTokenInfoResponse.id()); - Member loginMember = member.orElse(signUp(authorizationTokenInfoResponse.id())); + Member loginMember = member.orElseGet(() -> signUp(authorizationTokenInfoResponse.id())); - return MemberMapper.toLoginResponse(loginMember.getId(), member.isEmpty()); + return MemberMapper.toLoginResponse(loginMember, member.isEmpty()); } private Member signUp(Long socialId) { diff --git a/src/main/java/com/moabam/api/domain/entity/Member.java b/src/main/java/com/moabam/api/domain/entity/Member.java index fb866e07..13029124 100644 --- a/src/main/java/com/moabam/api/domain/entity/Member.java +++ b/src/main/java/com/moabam/api/domain/entity/Member.java @@ -82,7 +82,7 @@ public class Member extends BaseTimeEntity { @Builder private Member(Long id, Long socialId, String nickname, Bug bug) { this.id = id; - this.socialId = socialId; + this.socialId = requireNonNull(socialId); this.nickname = requireNonNull(nickname); this.profileImage = BaseImageUrl.PROFILE_URL; this.bug = requireNonNull(bug); diff --git a/src/main/java/com/moabam/api/dto/OAuthMapper.java b/src/main/java/com/moabam/api/dto/AuthorizationMapper.java similarity index 60% rename from src/main/java/com/moabam/api/dto/OAuthMapper.java rename to src/main/java/com/moabam/api/dto/AuthorizationMapper.java index a637ff4e..8b6ceeae 100644 --- a/src/main/java/com/moabam/api/dto/OAuthMapper.java +++ b/src/main/java/com/moabam/api/dto/AuthorizationMapper.java @@ -1,12 +1,15 @@ package com.moabam.api.dto; +import org.json.JSONObject; + +import com.moabam.api.domain.entity.enums.Role; import com.moabam.global.config.OAuthConfig; import lombok.AccessLevel; import lombok.NoArgsConstructor; @NoArgsConstructor(access = AccessLevel.PRIVATE) -public final class OAuthMapper { +public final class AuthorizationMapper { public static AuthorizationCodeRequest toAuthorizationCodeRequest(OAuthConfig oAuthConfig) { return AuthorizationCodeRequest.builder() @@ -25,4 +28,16 @@ public static AuthorizationTokenRequest toAuthorizationTokenRequest(OAuthConfig .clientSecret(oAuthConfig.client().clientSecret()) .build(); } + + public static PublicClaim toPublicClaim(JSONObject jsonObject) { + return PublicClaim.builder() + .id(Long.valueOf(jsonObject.get("id").toString())) + .nickname(jsonObject.getString("nickname")) + .role(jsonObject.getEnum(Role.class, "role")) + .build(); + } + + public static AuthorizationMember toAuthorizationMember(PublicClaim publicClaim) { + return new AuthorizationMember(publicClaim.id(), publicClaim.nickname(), publicClaim.role()); + } } diff --git a/src/main/java/com/moabam/api/dto/AuthorizationMember.java b/src/main/java/com/moabam/api/dto/AuthorizationMember.java new file mode 100644 index 00000000..2924d307 --- /dev/null +++ b/src/main/java/com/moabam/api/dto/AuthorizationMember.java @@ -0,0 +1,11 @@ +package com.moabam.api.dto; + +import com.moabam.api.domain.entity.enums.Role; + +public record AuthorizationMember( + Long id, + String nickname, + Role role +) { + +} diff --git a/src/main/java/com/moabam/api/dto/LoginResponse.java b/src/main/java/com/moabam/api/dto/LoginResponse.java index 49e13cc9..4d22458e 100644 --- a/src/main/java/com/moabam/api/dto/LoginResponse.java +++ b/src/main/java/com/moabam/api/dto/LoginResponse.java @@ -1,11 +1,13 @@ package com.moabam.api.dto; +import com.fasterxml.jackson.annotation.JsonUnwrapped; + import lombok.Builder; @Builder public record LoginResponse( - Long id, - boolean isSignUp + boolean isSignUp, + @JsonUnwrapped PublicClaim publicClaim ) { } diff --git a/src/main/java/com/moabam/api/dto/MemberMapper.java b/src/main/java/com/moabam/api/dto/MemberMapper.java index 2802b2ba..84646532 100644 --- a/src/main/java/com/moabam/api/dto/MemberMapper.java +++ b/src/main/java/com/moabam/api/dto/MemberMapper.java @@ -17,15 +17,13 @@ public static Member toMember(Long socialId, String nickName) { .build(); } - public static LoginResponse toLoginResponse(Long memberId) { + public static LoginResponse toLoginResponse(Member member, boolean isSignUp) { return LoginResponse.builder() - .id(memberId) - .build(); - } - - public static LoginResponse toLoginResponse(Long memberId, boolean isSignUp) { - return LoginResponse.builder() - .id(memberId) + .publicClaim(PublicClaim.builder() + .id(member.getId()) + .nickname(member.getNickname()) + .role(member.getRole()) + .build()) .isSignUp(isSignUp) .build(); } diff --git a/src/main/java/com/moabam/api/dto/PathMapper.java b/src/main/java/com/moabam/api/dto/PathMapper.java new file mode 100644 index 00000000..941fc731 --- /dev/null +++ b/src/main/java/com/moabam/api/dto/PathMapper.java @@ -0,0 +1,43 @@ +package com.moabam.api.dto; + +import static java.util.Objects.*; + +import java.util.List; + +import org.springframework.http.HttpMethod; + +import com.moabam.api.domain.entity.enums.Role; +import com.moabam.global.common.handler.PathResolver; + +import jakarta.annotation.Nonnull; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class PathMapper { + + public static PathResolver.Path parsePath(String uri) { + return parsePath(uri, null, null); + } + + public static PathResolver.Path parsePath(String uri, @Nonnull List params) { + if (!params.isEmpty() && params.get(0) instanceof Role) { + return parsePath(uri, (List)params, null); + } + return parsePath(uri, null, (List)params); + } + + private static PathResolver.Path parsePath(String uri, List roles, List methods) { + PathResolver.Path.PathBuilder pathBuilder = PathResolver.Path.builder().uri(uri); + + if (nonNull(roles)) { + pathBuilder.roles(roles); + } + + if (nonNull(methods)) { + pathBuilder.httpMethods(methods); + } + + return pathBuilder.build(); + } +} diff --git a/src/main/java/com/moabam/api/dto/PublicClaim.java b/src/main/java/com/moabam/api/dto/PublicClaim.java new file mode 100644 index 00000000..e67c4fc5 --- /dev/null +++ b/src/main/java/com/moabam/api/dto/PublicClaim.java @@ -0,0 +1,15 @@ +package com.moabam.api.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.moabam.api.domain.entity.enums.Role; + +import lombok.Builder; + +@Builder +public record PublicClaim( + Long id, + @JsonIgnore String nickname, + @JsonIgnore Role role +) { + +} diff --git a/src/main/java/com/moabam/api/presentation/MemberController.java b/src/main/java/com/moabam/api/presentation/MemberController.java index 20ec8018..f7a96591 100644 --- a/src/main/java/com/moabam/api/presentation/MemberController.java +++ b/src/main/java/com/moabam/api/presentation/MemberController.java @@ -7,7 +7,7 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -import com.moabam.api.application.AuthenticationService; +import com.moabam.api.application.AuthorizationService; import com.moabam.api.dto.AuthorizationCodeResponse; import com.moabam.api.dto.AuthorizationTokenInfoResponse; import com.moabam.api.dto.AuthorizationTokenResponse; @@ -21,21 +21,21 @@ @RequiredArgsConstructor public class MemberController { - private final AuthenticationService authenticationService; + private final AuthorizationService authorizationService; @GetMapping public void socialLogin(HttpServletResponse httpServletResponse) { - authenticationService.redirectToLoginPage(httpServletResponse); + authorizationService.redirectToLoginPage(httpServletResponse); } @GetMapping("/login/kakao/oauth") @ResponseStatus(HttpStatus.OK) public LoginResponse authorizationTokenIssue(@ModelAttribute AuthorizationCodeResponse authorizationCodeResponse, HttpServletResponse httpServletResponse) { - AuthorizationTokenResponse tokenResponse = authenticationService.requestToken(authorizationCodeResponse); + AuthorizationTokenResponse tokenResponse = authorizationService.requestToken(authorizationCodeResponse); AuthorizationTokenInfoResponse authorizationTokenInfoResponse = - authenticationService.requestTokenInfo(tokenResponse); + authorizationService.requestTokenInfo(tokenResponse); - return authenticationService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); + return authorizationService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); } } diff --git a/src/main/java/com/moabam/global/common/annotation/CurrentMember.java b/src/main/java/com/moabam/global/common/annotation/CurrentMember.java new file mode 100644 index 00000000..2d094ced --- /dev/null +++ b/src/main/java/com/moabam/global/common/annotation/CurrentMember.java @@ -0,0 +1,12 @@ +package com.moabam.global.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface CurrentMember { + +} diff --git a/src/main/java/com/moabam/global/common/handler/CurrentMemberArgumentResolver.java b/src/main/java/com/moabam/global/common/handler/CurrentMemberArgumentResolver.java new file mode 100644 index 00000000..1d0d9760 --- /dev/null +++ b/src/main/java/com/moabam/global/common/handler/CurrentMemberArgumentResolver.java @@ -0,0 +1,31 @@ +package com.moabam.global.common.handler; + +import static com.moabam.global.common.util.AuthorizationThreadLocal.*; + +import java.util.Objects; + +import javax.annotation.Nullable; + +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import com.moabam.api.dto.AuthorizationMember; +import com.moabam.global.common.annotation.CurrentMember; + +public class CurrentMemberArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return Objects.nonNull(parameter.getParameterAnnotation(CurrentMember.class)) + && parameter.getParameterType().equals(AuthorizationMember.class); + } + + @Override + public Object resolveArgument(@Nullable MethodParameter parameter, ModelAndViewContainer mavContainer, + @Nullable NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { + return getAuthorizationMember(); + } +} diff --git a/src/main/java/com/moabam/global/common/handler/PathResolver.java b/src/main/java/com/moabam/global/common/handler/PathResolver.java new file mode 100644 index 00000000..76ab8cb4 --- /dev/null +++ b/src/main/java/com/moabam/global/common/handler/PathResolver.java @@ -0,0 +1,85 @@ +package com.moabam.global.common.handler; + +import static java.util.Objects.*; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.springframework.http.HttpMethod; +import org.springframework.http.server.PathContainer; +import org.springframework.web.util.pattern.PathPattern; +import org.springframework.web.util.pattern.PathPatternParser; + +import com.moabam.api.domain.entity.enums.Role; + +import lombok.Builder; +import lombok.Singular; + +public class PathResolver { + + private final Map permitPatterns; + private final Map authenticationPatterns; + + public PathResolver(Paths paths) { + this.permitPatterns = Paths.pathParser(paths.permitAll); + this.authenticationPatterns = Paths.pathParser(paths.authentications); + } + + public Optional permitPathMatch(String uri) { + return match(permitPatterns, uri); + } + + public Optional authenticationsPatterns(String uri) { + return match(authenticationPatterns, uri); + } + + private Optional match(Map patterns, String uri) { + Set paths = patterns.keySet(); + PathContainer path = PathContainer.parsePath(uri); + PathPattern matchedPattern = paths.stream() + .filter(pathPattern -> pathPattern.matches(path)) + .findAny() + .orElse(null); + + return Optional.ofNullable(patterns.get(matchedPattern)); + } + + @Builder + public record Paths( + @Singular("permitOne") List permitAll, + @Singular("authentication") List authentications + ) { + + static Map pathParser(List uris) { + PathPatternParser parser = new PathPatternParser(); + return uris.stream() + .collect(Collectors.toMap( + path -> parser.parse(path.uri()), Function.identity() + )); + } + } + + public record Path( + String uri, + List httpMethods, + List roles + ) { + + private static final List BASE_METHODS = + List.of(HttpMethod.GET, HttpMethod.POST, HttpMethod.DELETE, HttpMethod.PUT, HttpMethod.PATCH); + + @Builder + public Path(String uri, @Singular("httpMethod") List httpMethods, + @Singular("role") List roles) { + this.uri = requireNonNull(uri); + this.roles = Optional.of(roles).filter(role -> !role.isEmpty()).orElse(List.of(Role.USER)); + this.httpMethods = Optional.of(httpMethods) + .filter(httpMethod -> !httpMethod.isEmpty()) + .orElse(BASE_METHODS); + } + } +} diff --git a/src/main/java/com/moabam/global/common/util/AuthorizationThreadLocal.java b/src/main/java/com/moabam/global/common/util/AuthorizationThreadLocal.java new file mode 100644 index 00000000..0174b764 --- /dev/null +++ b/src/main/java/com/moabam/global/common/util/AuthorizationThreadLocal.java @@ -0,0 +1,28 @@ +package com.moabam.global.common.util; + +import com.moabam.api.dto.AuthorizationMember; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class AuthorizationThreadLocal { + + private static final ThreadLocal authorizationMember; + + static { + authorizationMember = new ThreadLocal<>(); + } + + public static void setAuthorizationMember(AuthorizationMember authorizationMember) { + AuthorizationThreadLocal.authorizationMember.set(authorizationMember); + } + + public static AuthorizationMember getAuthorizationMember() { + return authorizationMember.get(); + } + + public static void remove() { + authorizationMember.remove(); + } +} diff --git a/src/main/java/com/moabam/global/common/util/CookieUtils.java b/src/main/java/com/moabam/global/common/util/CookieUtils.java index b7ece395..c1220718 100644 --- a/src/main/java/com/moabam/global/common/util/CookieUtils.java +++ b/src/main/java/com/moabam/global/common/util/CookieUtils.java @@ -7,11 +7,20 @@ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class CookieUtils { - public static Cookie tokenCookie(String name, String value) { + public static Cookie tokenCookie(String name, String value, long expireTime) { + return basic(name, value, expireTime); + } + + public static Cookie typeCookie(String value, long expireTime) { + return basic("token_type", value, expireTime); + } + + private static Cookie basic(String name, String value, long expireTime) { Cookie cookie = new Cookie(name, value); cookie.setSecure(true); cookie.setHttpOnly(true); cookie.setPath("/"); + cookie.setMaxAge((int)expireTime); return cookie; } diff --git a/src/main/java/com/moabam/global/config/WebConfig.java b/src/main/java/com/moabam/global/config/WebConfig.java index 2f276c96..166265d5 100644 --- a/src/main/java/com/moabam/global/config/WebConfig.java +++ b/src/main/java/com/moabam/global/config/WebConfig.java @@ -1,9 +1,17 @@ package com.moabam.global.config; +import java.util.List; + +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import com.moabam.api.dto.PathMapper; +import com.moabam.global.common.handler.CurrentMemberArgumentResolver; +import com.moabam.global.common.handler.PathResolver; + @Configuration public class WebConfig implements WebMvcConfigurer { @@ -20,4 +28,26 @@ public void addCorsMappings(final CorsRegistry registry) { .allowCredentials(true) .maxAge(3600); } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(handlerMethodArgumentResolver()); + } + + @Bean + public HandlerMethodArgumentResolver handlerMethodArgumentResolver() { + return new CurrentMemberArgumentResolver(); + } + + @Bean + public PathResolver pathResolver() { + PathResolver.Paths path = PathResolver.Paths.builder() + .permitAll(List.of( + PathMapper.parsePath("/members"), + PathMapper.parsePath("/members/login/*/oauth") + )) + .build(); + + return new PathResolver(path); + } } diff --git a/src/main/java/com/moabam/global/error/model/ErrorMessage.java b/src/main/java/com/moabam/global/error/model/ErrorMessage.java index 8efa2059..6a9904f6 100644 --- a/src/main/java/com/moabam/global/error/model/ErrorMessage.java +++ b/src/main/java/com/moabam/global/error/model/ErrorMessage.java @@ -26,7 +26,7 @@ public enum ErrorMessage { LOGIN_FAILED("로그인에 실패했습니다."), REQUEST_FAILED("네트워크 접근 실패입니다."), GRANT_FAILED("인가 코드 실패"), - AUTHENTICATIE_FAIL("인증 실패"), + AUTHENTICATE_FAIL("인증 실패"), MEMBER_NOT_FOUND("존재하지 않는 회원입니다."), MEMBER_ROOM_EXCEED("참여할 수 있는 방의 개수가 모두 찼습니다."), diff --git a/src/main/java/com/moabam/global/filter/AuthorizationFilter.java b/src/main/java/com/moabam/global/filter/AuthorizationFilter.java new file mode 100644 index 00000000..5aeea6fc --- /dev/null +++ b/src/main/java/com/moabam/global/filter/AuthorizationFilter.java @@ -0,0 +1,113 @@ +package com.moabam.global.filter; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; + +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.HandlerExceptionResolver; + +import com.moabam.api.application.AuthorizationService; +import com.moabam.api.application.JwtAuthenticationService; +import com.moabam.api.dto.AuthorizationMapper; +import com.moabam.api.dto.PublicClaim; +import com.moabam.global.common.util.AuthorizationThreadLocal; +import com.moabam.global.error.exception.UnauthorizedException; +import com.moabam.global.error.model.ErrorMessage; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Order(2) +@Component +@RequiredArgsConstructor +public class AuthorizationFilter extends OncePerRequestFilter { + + private final HandlerExceptionResolver handlerExceptionResolver; + private final JwtAuthenticationService authenticationService; + private final AuthorizationService authorizationService; + + @Override + protected void doFilterInternal(@NotNull HttpServletRequest httpServletRequest, + @NotNull HttpServletResponse httpServletResponse, + @NotNull FilterChain filterChain) throws ServletException, IOException { + + if (isPermit(httpServletRequest)) { + filterChain.doFilter(httpServletRequest, httpServletResponse); + return; + } + + try { + invoke(httpServletRequest, httpServletResponse); + } catch (UnauthorizedException unauthorizedException) { + log.error("Login Failed"); + handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse, null, + unauthorizedException); + + return; + } + + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + + private boolean isPermit(HttpServletRequest httpServletRequest) { + Boolean isPermit = (Boolean)httpServletRequest.getAttribute("isPermit"); + + return Objects.nonNull(isPermit) && Boolean.TRUE.equals(isPermit); + } + + private void invoke(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + Cookie[] cookies = getCookiesOrThrow(httpServletRequest); + + if (!isTokenTypeBearer(cookies)) { + throw new UnauthorizedException(ErrorMessage.GRANT_FAILED); + } + + handleTokenAuthenticate(cookies, httpServletResponse); + } + + private boolean isTokenTypeBearer(Cookie[] cookies) { + return "Bearer".equals(extractTokenFromCookie(cookies, "token_type")); + } + + private void handleTokenAuthenticate(Cookie[] cookies, + HttpServletResponse httpServletResponse) { + String accessToken = extractTokenFromCookie(cookies, "access_token"); + PublicClaim publicClaim = authenticationService.parseClaim(accessToken); + + if (authenticationService.isTokenExpire(accessToken)) { + String refreshToken = extractTokenFromCookie(cookies, "refresh_token"); + + if (authenticationService.isTokenExpire(refreshToken)) { + throw new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL); + } + + authorizationService.issueServiceToken(httpServletResponse, publicClaim); + } + + AuthorizationThreadLocal.setAuthorizationMember(AuthorizationMapper.toAuthorizationMember(publicClaim)); + } + + private Cookie[] getCookiesOrThrow(HttpServletRequest httpServletRequest) { + return Optional.ofNullable(httpServletRequest.getCookies()) + .orElseThrow(() -> new UnauthorizedException(ErrorMessage.GRANT_FAILED)); + } + + private String extractTokenFromCookie(Cookie[] cookies, String tokenName) { + return Arrays.stream(cookies) + .filter(cookie -> tokenName.equals(cookie.getName())) + .map(Cookie::getValue) + .findFirst() + .orElseThrow(() -> new UnauthorizedException(ErrorMessage.AUTHENTICATE_FAIL)); + } +} diff --git a/src/main/java/com/moabam/global/filter/PathFilter.java b/src/main/java/com/moabam/global/filter/PathFilter.java new file mode 100644 index 00000000..4baf8aa9 --- /dev/null +++ b/src/main/java/com/moabam/global/filter/PathFilter.java @@ -0,0 +1,39 @@ +package com.moabam.global.filter; + +import java.io.IOException; +import java.util.Optional; + +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import com.moabam.global.common.handler.PathResolver; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; + +@Order(1) +@Component +@RequiredArgsConstructor +public class PathFilter extends OncePerRequestFilter { + + private final PathResolver pathResolver; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + Optional matchedPath = pathResolver.permitPathMatch(request.getRequestURI()); + + matchedPath.ifPresent(path -> { + if (path.httpMethods().stream() + .anyMatch(httpMethod -> httpMethod.matches(request.getMethod()))) { + request.setAttribute("isPermit", true); + } + }); + + filterChain.doFilter(request, response); + } +} diff --git a/src/main/resources/static/docs/coupon.html b/src/main/resources/static/docs/coupon.html index 56edb977..b3b98263 100644 --- a/src/main/resources/static/docs/coupon.html +++ b/src/main/resources/static/docs/coupon.html @@ -1,467 +1,2142 @@ - - - - -쿠폰(Coupon) - - + + + + + 쿠폰(Coupon) + +
-
-

쿠폰(Coupon)

-
-
-
-
쿠폰에 대해 생성/삭제/조회/발급/사용 기능을 제공합니다.
-
-
-
-
-

쿠폰 생성

-
-
-
관리자가 쿠폰을 생성합니다.
-
-
-

요청

-
-
+
+

쿠폰(Coupon)

+
+
+
+
쿠폰에 대해 생성/삭제/조회/발급/사용 기능을 제공합니다.
+
+
+
+
+

쿠폰 생성

+
+
+
관리자가 쿠폰을 생성합니다.
+
+
+

요청

+
+
POST /admins/coupons HTTP/1.1
 Content-Type: application/json;charset=UTF-8
+<<<<<<< HEAD
+Content-Length: 194
+=======
 Content-Length: 192
+>>>>>>> b5f9a450e8eb3b4f4527d412d519e6afc9c16365
 Host: localhost:8080
 
 {
@@ -473,74 +2148,74 @@ 

요청

"startAt" : "2023-01-01T00:00", "endAt" : "2023-02-01T00:00" }
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 409 Conflict
 Vary: Origin
 Vary: Access-Control-Request-Method
 Vary: Access-Control-Request-Headers
 Content-Type: application/json
-Content-Length: 62
+Content-Length: 64
 
 {
   "message" : "쿠폰의 이름이 중복되었습니다."
 }
-
-
-
-
-
-

쿠폰 삭제

-
-
-
관리자가 쿠폰 ID와 일치하는 쿠폰을 삭제합니다.
-
-
-

요청

-
-
+
+
+
+
+
+

쿠폰 삭제

+
+
+
관리자가 쿠폰 ID와 일치하는 쿠폰을 삭제합니다.
+
+
+

요청

+
+
DELETE /admins/coupons/77777777777 HTTP/1.1
 Host: localhost:8080
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 404 Not Found
 Vary: Origin
 Vary: Access-Control-Request-Method
 Vary: Access-Control-Request-Headers
 Content-Type: application/json
-Content-Length: 56
+Content-Length: 58
 
 {
   "message" : "존재하지 않는 쿠폰입니다."
 }
-
-
-
-
-
-

특정 쿠폰 조회

-
-
-
관리자 혹은 사용자가 특정 ID와 일치하는 쿠폰을 조회합니다.
-
-
-
-

요청

-
-
+
+
+
+
+
+

특정 쿠폰 조회

+
+
+
관리자 혹은 사용자가 특정 ID와 일치하는 쿠폰을 조회합니다.
+
+
+
+

요청

+
+
GET /coupons/77777777777 HTTP/1.1
 Host: localhost:8080
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 404 Not Found
 Vary: Origin
 Vary: Access-Control-Request-Method
@@ -551,22 +2226,22 @@ 

응답

{ "message" : "존재하지 않는 쿠폰입니다." }
-
-
-
-
-
-
-

상태에 따른 쿠폰들을 조회

-
-
-
관리자 혹은 사용자가 날짜 상태에 따라 쿠폰들을 조회합니다.
-
-
-
-

요청

-
-
+
+
+
+
+
+
+

상태에 따른 쿠폰들을 조회

+
+
+
관리자 혹은 사용자가 날짜 상태에 따라 쿠폰들을 조회합니다.
+
+
+
+

요청

+
+
POST /coupons/search HTTP/1.1
 Content-Type: application/json;charset=UTF-8
 Content-Length: 84
@@ -577,11 +2252,11 @@ 

요청

"couponNotStarted" : false, "couponEnded" : false }
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 200 OK
 Vary: Origin
 Vary: Access-Control-Request-Method
@@ -590,45 +2265,45 @@ 

응답

Content-Length: 3 [ ]
-
-
-
-
-
-
-

특정 사용자의 쿠폰 보관함을 조회

-
-
-
사용자가 자신의 보관함에 있는 쿠폰들을 조회합니다.
-
-
-
-
-
-

쿠폰 발급 (진행 중)

-
-
-
사용자가 발급 가능한 쿠폰을 선착순으로 발급 받습니다.
-
-
-
-
-
-

쿠폰 사용 (진행 중)

-
-
-
사용자가 자신의 보관함에 있는 쿠폰들을 사용합니다.
-
-
-
-
-
+
+
+
+
+
+
+

특정 사용자의 쿠폰 보관함을 조회

+
+
+
사용자가 자신의 보관함에 있는 쿠폰들을 조회합니다.
+
+
+
+
+
+

쿠폰 발급 (진행 중)

+
+
+
사용자가 발급 가능한 쿠폰을 선착순으로 발급 받습니다.
+
+
+
+
+
+

쿠폰 사용 (진행 중)

+
+
+
사용자가 자신의 보관함에 있는 쿠폰들을 사용합니다.
+
+
+
+
+
- \ No newline at end of file + diff --git a/src/main/resources/static/docs/index.html b/src/main/resources/static/docs/index.html index 62f80fd6..3e40058e 100644 --- a/src/main/resources/static/docs/index.html +++ b/src/main/resources/static/docs/index.html @@ -616,7 +616,7 @@

diff --git a/src/main/resources/static/docs/notification.html b/src/main/resources/static/docs/notification.html index 206bb94a..b82bdbff 100644 --- a/src/main/resources/static/docs/notification.html +++ b/src/main/resources/static/docs/notification.html @@ -1,494 +1,2165 @@ - - - - -알림(Notification) - - + + + + + 알림(Notification) + +
-
-

알림(Notification)

-
-
-
-
콕 찌르기 알림 기능을 제공합니다.
-
-
-
-

콕 찌르기 알림

-
-
+
+

알림(Notification)

+
+
+
+
콕 찌르기 알림 기능을 제공합니다.
+
+
+
+

콕 찌르기 알림

+
+
1) 특정 방의 사용자가 다른 사용자를 콕 찌릅니다.
 2) 서버에서 콕 찌를 대상의 FCM Token 여부를 검증합니다.
 3) Firebase 서버에 FCM Push Messaing 알림을 비동기로 요청합니다.
 4) Firebase 서버에서 FCM Token으로 식별된 기기에 알림을 보냅니다.
-
-
-

요청

-
-
+
+
+

요청

+
+
GET /notifications/rooms/3/members/3 HTTP/1.1
 Host: localhost:8080
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 409 Conflict
 Vary: Origin
 Vary: Access-Control-Request-Method
 Vary: Access-Control-Request-Headers
 Content-Type: application/json
-Content-Length: 64
+Content-Length: 66
 
 {
   "message" : "이미 콕 알림을 보낸 대상입니다."
 }
-
-
-
-
-
+
+
+
+
+
- \ No newline at end of file + diff --git a/src/test/java/com/moabam/api/application/AuthenticationServiceTest.java b/src/test/java/com/moabam/api/application/AuthorizationServiceTest.java similarity index 77% rename from src/test/java/com/moabam/api/application/AuthenticationServiceTest.java rename to src/test/java/com/moabam/api/application/AuthorizationServiceTest.java index 57afb71a..e83f0e43 100644 --- a/src/test/java/com/moabam/api/application/AuthenticationServiceTest.java +++ b/src/test/java/com/moabam/api/application/AuthorizationServiceTest.java @@ -24,12 +24,14 @@ import com.moabam.api.dto.AuthorizationCodeRequest; import com.moabam.api.dto.AuthorizationCodeResponse; +import com.moabam.api.dto.AuthorizationMapper; import com.moabam.api.dto.AuthorizationTokenInfoResponse; import com.moabam.api.dto.AuthorizationTokenRequest; import com.moabam.api.dto.AuthorizationTokenResponse; import com.moabam.api.dto.LoginResponse; -import com.moabam.api.dto.OAuthMapper; +import com.moabam.api.dto.PublicClaim; import com.moabam.global.config.OAuthConfig; +import com.moabam.global.config.TokenConfig; import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.model.ErrorMessage; import com.moabam.support.fixture.AuthorizationResponseFixture; @@ -37,10 +39,10 @@ import jakarta.servlet.http.Cookie; @ExtendWith(MockitoExtension.class) -class AuthenticationServiceTest { +class AuthorizationServiceTest { @InjectMocks - AuthenticationService authenticationService; + AuthorizationService authorizationService; @Mock OAuth2AuthorizationServerRequestService oAuth2AuthorizationServerRequestService; @@ -52,24 +54,30 @@ class AuthenticationServiceTest { JwtProviderService jwtProviderService; OAuthConfig oauthConfig; - AuthenticationService noPropertyService; + TokenConfig tokenConfig; + AuthorizationService noPropertyService; OAuthConfig noOAuthConfig; @BeforeEach public void initParams() { + tokenConfig = new TokenConfig(null, 100000, 150000, + "testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest"); + ReflectionTestUtils.setField(authorizationService, "tokenConfig", tokenConfig); + oauthConfig = new OAuthConfig( new OAuthConfig.Provider("https://authorization/url", "http://redirect/url", "http://token/url", "http://tokenInfo/url"), new OAuthConfig.Client("provider", "testtestetsttest", "testtesttest", "authorization_code", List.of("profile_nickname", "profile_image")) ); - ReflectionTestUtils.setField(authenticationService, "oAuthConfig", oauthConfig); + ReflectionTestUtils.setField(authorizationService, "oAuthConfig", oauthConfig); noOAuthConfig = new OAuthConfig( new OAuthConfig.Provider(null, null, null, null), new OAuthConfig.Client(null, null, null, null, null) ); - noPropertyService = new AuthenticationService(noOAuthConfig, oAuth2AuthorizationServerRequestService, + noPropertyService = new AuthorizationService(noOAuthConfig, tokenConfig, + oAuth2AuthorizationServerRequestService, memberService, jwtProviderService); } @@ -77,7 +85,7 @@ public void initParams() { @Test void authorization_code_request_mapping_fail() { // When + Then - Assertions.assertThatThrownBy(() -> OAuthMapper.toAuthorizationCodeRequest(noOAuthConfig)) + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationCodeRequest(noOAuthConfig)) .isInstanceOf(NullPointerException.class); } @@ -85,7 +93,7 @@ void authorization_code_request_mapping_fail() { @Test void authorization_code_request_mapping_success() { // Given - AuthorizationCodeRequest authorizationCodeRequest = OAuthMapper.toAuthorizationCodeRequest(oauthConfig); + AuthorizationCodeRequest authorizationCodeRequest = AuthorizationMapper.toAuthorizationCodeRequest(oauthConfig); // When + Then assertThat(authorizationCodeRequest).isNotNull(); @@ -102,7 +110,7 @@ void redirect_loginPage_success() { MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); // when - authenticationService.redirectToLoginPage(mockHttpServletResponse); + authorizationService.redirectToLoginPage(mockHttpServletResponse); // then verify(oAuth2AuthorizationServerRequestService).loginRequest(eq(mockHttpServletResponse), anyString()); @@ -116,7 +124,7 @@ void authorization_grant_fail() { "errorDescription", null); // When + Then - assertThatThrownBy(() -> authenticationService.requestToken(authorizationCodeResponse)) + assertThatThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)) .isInstanceOf(BadRequestException.class) .hasMessage(ErrorMessage.GRANT_FAILED.getMessage()); } @@ -135,14 +143,14 @@ void authorization_grant_success() { new ResponseEntity<>(authorizationTokenResponse, HttpStatus.OK)); // When + Then - assertThatNoException().isThrownBy(() -> authenticationService.requestToken(authorizationCodeResponse)); + assertThatNoException().isThrownBy(() -> authorizationService.requestToken(authorizationCodeResponse)); } @DisplayName("토큰 요청 매퍼 실패 - code null") @Test void token_request_mapping_failBy_code() { // When + Then - Assertions.assertThatThrownBy(() -> OAuthMapper.toAuthorizationTokenRequest(oauthConfig, null)) + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(oauthConfig, null)) .isInstanceOf(NullPointerException.class); } @@ -150,7 +158,7 @@ void token_request_mapping_failBy_code() { @Test void token_request_mapping_failBy_config() { // When + Then - Assertions.assertThatThrownBy(() -> OAuthMapper.toAuthorizationTokenRequest(noOAuthConfig, "Test")) + Assertions.assertThatThrownBy(() -> AuthorizationMapper.toAuthorizationTokenRequest(noOAuthConfig, "Test")) .isInstanceOf(NullPointerException.class); } @@ -159,7 +167,8 @@ void token_request_mapping_failBy_config() { void token_request_mapping_success() { // Given String code = "Test"; - AuthorizationTokenRequest authorizationTokenRequest = OAuthMapper.toAuthorizationTokenRequest(oauthConfig, + AuthorizationTokenRequest authorizationTokenRequest = AuthorizationMapper.toAuthorizationTokenRequest( + oauthConfig, code); // When + Then @@ -186,7 +195,7 @@ void generate_token() { .thenReturn(new ResponseEntity<>(tokenInfoResponse, HttpStatus.OK)); // Then - assertThatNoException().isThrownBy(() -> authenticationService.requestTokenInfo(tokenResponse)); + assertThatNoException().isThrownBy(() -> authorizationService.requestTokenInfo(tokenResponse)); } @DisplayName("회원 가입 및 로그인 성공 테스트") @@ -198,7 +207,10 @@ void signUp_success(boolean isSignUp) { AuthorizationTokenInfoResponse authorizationTokenInfoResponse = AuthorizationResponseFixture.authorizationTokenInfoResponse(); LoginResponse loginResponse = LoginResponse.builder() - .id(1L) + .publicClaim(PublicClaim.builder() + .id(1L) + .nickname("nickname") + .build()) .isSignUp(isSignUp) .build(); @@ -206,11 +218,14 @@ void signUp_success(boolean isSignUp) { // when LoginResponse result = - authenticationService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); + authorizationService.signUpOrLogin(httpServletResponse, authorizationTokenInfoResponse); // then assertThat(loginResponse).isEqualTo(result); - assertThat(httpServletResponse.getHeader("token_type")).isEqualTo("Bearer"); + + Cookie tokenType = httpServletResponse.getCookie("token_type"); + assertThat(tokenType).isNotNull(); + assertThat(tokenType.getValue()).isEqualTo("Bearer"); Cookie accessCookie = httpServletResponse.getCookie("access_token"); assertThat(accessCookie).isNotNull(); diff --git a/src/test/java/com/moabam/api/application/JwtAuthenticationServiceTest.java b/src/test/java/com/moabam/api/application/JwtAuthenticationServiceTest.java index 1c4df207..addbd4fd 100644 --- a/src/test/java/com/moabam/api/application/JwtAuthenticationServiceTest.java +++ b/src/test/java/com/moabam/api/application/JwtAuthenticationServiceTest.java @@ -1,6 +1,7 @@ package com.moabam.api.application; import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; import java.nio.charset.StandardCharsets; import java.security.Key; @@ -15,8 +16,10 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; +import com.moabam.api.dto.PublicClaim; import com.moabam.global.config.TokenConfig; import com.moabam.global.error.exception.UnauthorizedException; +import com.moabam.support.fixture.PublicClaimFixture; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; @@ -42,21 +45,33 @@ void initConfig() { jwtAuthenticationService = new JwtAuthenticationService(tokenConfig); } + @DisplayName("토큰 인증 성공 테스트") + @Test + void token_authentication_success() { + // given + String token = jwtProviderService.provideAccessToken(PublicClaimFixture.publicClaim()); + + // when, then + assertThatNoException().isThrownBy(() -> + jwtAuthenticationService.isTokenExpire(token)); + } + @DisplayName("토큰 인증 시간 만료 테스트") @Test void token_authentication_time_expire() { // Given + PublicClaim publicClaim = PublicClaimFixture.publicClaim(); TokenConfig tokenConfig = new TokenConfig(originIss, 0, 0, originSecretKey); JwtAuthenticationService jwtAuthenticationService = new JwtAuthenticationService(tokenConfig); JwtProviderService jwtProviderService = new JwtProviderService(tokenConfig); - String token = jwtProviderService.provideAccessToken(originId); + String token = jwtProviderService.provideAccessToken(publicClaim); // When assertThatNoException().isThrownBy(() -> { - boolean result = jwtAuthenticationService.isTokenValid(token); + boolean result = jwtAuthenticationService.isTokenExpire(token); // Then - assertThat(result).isFalse(); + assertThat(result).isTrue(); }); } @@ -64,7 +79,9 @@ void token_authentication_time_expire() { @Test void token_authenticate_failBy_payload() { // Given - String token = jwtProviderService.provideAccessToken(originId); + PublicClaim publicClaim = PublicClaimFixture.publicClaim(); + + String token = jwtProviderService.provideAccessToken(publicClaim); String[] parts = token.split("\\."); String claims = new String(Base64.getDecoder().decode(parts[1])); @@ -79,7 +96,7 @@ void token_authenticate_failBy_payload() { parts[2]); // Then - Assertions.assertThatThrownBy(() -> jwtAuthenticationService.isTokenValid(newToken)) + Assertions.assertThatThrownBy(() -> jwtAuthenticationService.isTokenExpire(newToken)) .isInstanceOf(UnauthorizedException.class); } @@ -102,7 +119,25 @@ void token_authenticate_failBy_key() { .compact(); // When + Then - assertThatThrownBy(() -> jwtAuthenticationService.isTokenValid(token)) + assertThatThrownBy(() -> jwtAuthenticationService.isTokenExpire(token)) .isExactlyInstanceOf(UnauthorizedException.class); } + + @DisplayName("토큰을 PublicClaim으로 변환 성공") + @Test + void token_parse_to_public_claim() { + // given + PublicClaim publicClaim = PublicClaimFixture.publicClaim(); + String token = jwtProviderService.provideAccessToken(publicClaim); + + // when + PublicClaim parsedClaim = jwtAuthenticationService.parseClaim(token); + + // then + assertAll( + () -> assertThat(publicClaim.id()).isEqualTo(parsedClaim.id()), + () -> assertThat(publicClaim.nickname()).isEqualTo(parsedClaim.nickname()), + () -> assertThat(publicClaim.role()).isEqualTo(parsedClaim.role()) + ); + } } diff --git a/src/test/java/com/moabam/api/application/JwtProviderServiceTest.java b/src/test/java/com/moabam/api/application/JwtProviderServiceTest.java index e1d84262..c511145a 100644 --- a/src/test/java/com/moabam/api/application/JwtProviderServiceTest.java +++ b/src/test/java/com/moabam/api/application/JwtProviderServiceTest.java @@ -10,7 +10,9 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import com.moabam.api.dto.PublicClaim; import com.moabam.global.config.TokenConfig; +import com.moabam.support.fixture.PublicClaimFixture; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; @@ -29,9 +31,10 @@ void create_access_token_success() throws JSONException { TokenConfig tokenConfig = new TokenConfig("PARK", accessExpire, 0L, secretKey); JwtProviderService jwtProviderService = new JwtProviderService(tokenConfig); + PublicClaim publicClaim = PublicClaimFixture.publicClaim(); // when - String accessToken = jwtProviderService.provideAccessToken(id); + String accessToken = jwtProviderService.provideAccessToken(publicClaim); String[] parts = accessToken.split("\\."); String headers = new String(Base64.getDecoder().decode(parts[0])); @@ -62,7 +65,7 @@ void create_refresh_token_success() throws JSONException { JwtProviderService jwtProviderService = new JwtProviderService(tokenConfig); // when - String refreshToken = jwtProviderService.provideRefreshToken(id); + String refreshToken = jwtProviderService.provideRefreshToken(); String[] parts = refreshToken.split("\\."); String headers = new String(Base64.getDecoder().decode(parts[0])); @@ -91,9 +94,10 @@ void create_access_token_fail() { TokenConfig tokenConfig = new TokenConfig("PARK", accessExpire, 0L, secretKey); JwtProviderService jwtProviderService = new JwtProviderService(tokenConfig); + PublicClaim publicClaim = PublicClaimFixture.publicClaim(); // when - String accessToken = jwtProviderService.provideAccessToken(id); + String accessToken = jwtProviderService.provideAccessToken(publicClaim); // then assertThatThrownBy(() -> Jwts.parserBuilder() @@ -111,9 +115,10 @@ void create_token_fail() { TokenConfig tokenConfig = new TokenConfig("PARK", 0L, refreshExpire, secretKey); JwtProviderService jwtProviderService = new JwtProviderService(tokenConfig); + PublicClaim publicClaim = PublicClaimFixture.publicClaim(); // when - String accessToken = jwtProviderService.provideAccessToken(id); + String accessToken = jwtProviderService.provideAccessToken(publicClaim); // then assertThatThrownBy(() -> Jwts.parserBuilder() diff --git a/src/test/java/com/moabam/api/application/MemberServiceTest.java b/src/test/java/com/moabam/api/application/MemberServiceTest.java index f5ce03a0..1d3fc62b 100644 --- a/src/test/java/com/moabam/api/application/MemberServiceTest.java +++ b/src/test/java/com/moabam/api/application/MemberServiceTest.java @@ -42,7 +42,7 @@ void member_exist_and_login_success() { LoginResponse result = memberService.login(authorizationTokenInfoResponse); // then - assertThat(result.id()).isEqualTo(member.getId()); + assertThat(result.publicClaim().id()).isEqualTo(member.getId()); assertThat(result.isSignUp()).isFalse(); } @@ -64,7 +64,7 @@ void signUp_success() { LoginResponse result = memberService.login(authorizationTokenInfoResponse); // then - assertThat(authorizationTokenInfoResponse.id()).isEqualTo(result.id()); + assertThat(authorizationTokenInfoResponse.id()).isEqualTo(result.publicClaim().id()); assertThat(result.isSignUp()).isTrue(); } } diff --git a/src/test/java/com/moabam/api/domain/repository/MemberRepositoryTest.java b/src/test/java/com/moabam/api/domain/repository/MemberRepositoryTest.java new file mode 100644 index 00000000..9ae4b3f9 --- /dev/null +++ b/src/test/java/com/moabam/api/domain/repository/MemberRepositoryTest.java @@ -0,0 +1,32 @@ +package com.moabam.api.domain.repository; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import com.moabam.api.domain.entity.Member; +import com.moabam.support.annotation.QuerydslRepositoryTest; +import com.moabam.support.fixture.MemberFixture; + +@QuerydslRepositoryTest +class MemberRepositoryTest { + + @Autowired + MemberRepository memberRepository; + + @DisplayName("") + @Test + void test() { + // given + Member member = MemberFixture.member(); + memberRepository.save(member); + + // when + Member savedMember = memberRepository.findBySocialId(member.getSocialId()).orElse(null); + + // then + Assertions.assertThat(savedMember).isNotNull(); + + } +} diff --git a/src/test/java/com/moabam/api/presentation/BugControllerTest.java b/src/test/java/com/moabam/api/presentation/BugControllerTest.java index 4a78621a..4c6700d3 100644 --- a/src/test/java/com/moabam/api/presentation/BugControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/BugControllerTest.java @@ -20,9 +20,10 @@ import com.moabam.api.application.BugService; import com.moabam.api.dto.BugMapper; import com.moabam.api.dto.BugResponse; +import com.moabam.support.common.WithoutFilterSupporter; -@WebMvcTest(BugController.class) -class BugControllerTest { +@WebMvcTest(controllers = BugController.class) +class BugControllerTest extends WithoutFilterSupporter { @Autowired MockMvc mockMvc; diff --git a/src/test/java/com/moabam/api/presentation/CouponControllerTest.java b/src/test/java/com/moabam/api/presentation/CouponControllerTest.java index 819b149a..1fc2e129 100644 --- a/src/test/java/com/moabam/api/presentation/CouponControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/CouponControllerTest.java @@ -29,6 +29,7 @@ import com.moabam.api.dto.CouponSearchRequest; import com.moabam.api.dto.CreateCouponRequest; import com.moabam.global.error.model.ErrorMessage; +import com.moabam.support.common.WithoutFilterSupporter; import com.moabam.support.fixture.CouponFixture; import com.moabam.support.fixture.CouponSnippetFixture; import com.moabam.support.fixture.ErrorSnippetFixture; @@ -37,7 +38,7 @@ @SpringBootTest @AutoConfigureMockMvc @AutoConfigureRestDocs -class CouponControllerTest { +class CouponControllerTest extends WithoutFilterSupporter { @Autowired private MockMvc mockMvc; diff --git a/src/test/java/com/moabam/api/presentation/ItemControllerTest.java b/src/test/java/com/moabam/api/presentation/ItemControllerTest.java index 46e25c6d..3eaf9be1 100644 --- a/src/test/java/com/moabam/api/presentation/ItemControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/ItemControllerTest.java @@ -27,9 +27,11 @@ import com.moabam.api.dto.ItemMapper; import com.moabam.api.dto.ItemsResponse; import com.moabam.api.dto.PurchaseItemRequest; +import com.moabam.support.annotation.WithMember; +import com.moabam.support.common.WithoutFilterSupporter; -@WebMvcTest(ItemController.class) -class ItemControllerTest { +@WebMvcTest(controllers = ItemController.class) +class ItemControllerTest extends WithoutFilterSupporter { @Autowired MockMvc mockMvc; @@ -41,6 +43,7 @@ class ItemControllerTest { ItemService itemService; @DisplayName("아이템 목록을 조회한다.") + @WithMember @Test void get_items_success() throws Exception { // given @@ -52,9 +55,8 @@ void get_items_success() throws Exception { given(itemService.getItems(memberId, type)).willReturn(expected); // when, then - String content = mockMvc.perform(get("/items") - .param("type", ItemType.MORNING.name()) - .contentType(APPLICATION_JSON)) + String content = mockMvc.perform( + get("/items").param("type", ItemType.MORNING.name()).contentType(APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()) .andReturn() @@ -73,15 +75,13 @@ void purchase_item_success() throws Exception { PurchaseItemRequest request = new PurchaseItemRequest(BugType.MORNING); // when, then - mockMvc.perform(post("/items/{itemId}/purchase", itemId) - .contentType(APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))) - .andDo(print()) - .andExpect(status().isOk()); + mockMvc.perform(post("/items/{itemId}/purchase", itemId).contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))).andDo(print()).andExpect(status().isOk()); verify(itemService).purchaseItem(memberId, itemId, request); } @DisplayName("아이템을 적용한다.") + @WithMember @Test void select_item_success() throws Exception { // given @@ -89,8 +89,7 @@ void select_item_success() throws Exception { Long itemId = 1L; // when, then - mockMvc.perform(post("/items/{itemId}/select", itemId) - .contentType(APPLICATION_JSON)) + mockMvc.perform(post("/items/{itemId}/select", itemId).contentType(APPLICATION_JSON)) .andDo(print()) .andExpect(status().isOk()); verify(itemService).selectItem(memberId, itemId); diff --git a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java index 1c92c1a5..402017e8 100644 --- a/src/test/java/com/moabam/api/presentation/MemberControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/MemberControllerTest.java @@ -32,7 +32,7 @@ import org.springframework.web.util.UriComponentsBuilder; import com.fasterxml.jackson.databind.ObjectMapper; -import com.moabam.api.application.AuthenticationService; +import com.moabam.api.application.AuthorizationService; import com.moabam.api.application.OAuth2AuthorizationServerRequestService; import com.moabam.api.dto.AuthorizationCodeResponse; import com.moabam.api.dto.AuthorizationTokenInfoResponse; @@ -56,7 +56,7 @@ class MemberControllerTest { OAuth2AuthorizationServerRequestService oAuth2AuthorizationServerRequestService; @SpyBean - AuthenticationService authenticationService; + AuthorizationService authorizationService; @Autowired OAuthConfig oAuthConfig; @@ -137,7 +137,7 @@ void social_login_signUp_request_success() throws Exception { .andExpectAll( status().isOk(), MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON), - MockMvcResultMatchers.header().string("token_type", "Bearer"), + cookie().value("token_type", "Bearer"), cookie().exists("access_token"), cookie().httpOnly("access_token", true), cookie().secure("access_token", true), @@ -183,7 +183,7 @@ void token_info_response_fail(int code) throws Exception { // when doReturn(AuthorizationResponseFixture.authorizationTokenResponse()) - .when(authenticationService).requestToken(authorizationCodeResponse); + .when(authorizationService).requestToken(authorizationCodeResponse); // expected mockRestServiceServer.expect(requestTo(oAuthConfig.provider().tokenInfo())) diff --git a/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java b/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java index d2c848ac..f9b2a7e5 100644 --- a/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java @@ -30,6 +30,7 @@ import com.moabam.api.domain.repository.RoomRepository; import com.moabam.global.common.repository.StringRedisRepository; import com.moabam.global.error.model.ErrorMessage; +import com.moabam.support.common.WithoutFilterSupporter; import com.moabam.support.fixture.ErrorSnippetFixture; import com.moabam.support.fixture.MemberFixture; import com.moabam.support.fixture.RoomFixture; @@ -38,7 +39,7 @@ @SpringBootTest @AutoConfigureMockMvc @AutoConfigureRestDocs -class NotificationControllerTest { +class NotificationControllerTest extends WithoutFilterSupporter { @Autowired private MockMvc mockMvc; diff --git a/src/test/java/com/moabam/api/presentation/ProductControllerTest.java b/src/test/java/com/moabam/api/presentation/ProductControllerTest.java index 554b9b6b..991c75a5 100644 --- a/src/test/java/com/moabam/api/presentation/ProductControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/ProductControllerTest.java @@ -23,9 +23,10 @@ import com.moabam.api.domain.entity.Product; import com.moabam.api.dto.ProductMapper; import com.moabam.api.dto.ProductsResponse; +import com.moabam.support.common.WithoutFilterSupporter; -@WebMvcTest(ProductController.class) -class ProductControllerTest { +@WebMvcTest(controllers = ProductController.class) +class ProductControllerTest extends WithoutFilterSupporter { @Autowired MockMvc mockMvc; diff --git a/src/test/java/com/moabam/api/presentation/RoomControllerTest.java b/src/test/java/com/moabam/api/presentation/RoomControllerTest.java index e8c2113d..bd4926fe 100644 --- a/src/test/java/com/moabam/api/presentation/RoomControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/RoomControllerTest.java @@ -42,6 +42,8 @@ import com.moabam.api.dto.CreateRoomRequest; import com.moabam.api.dto.EnterRoomRequest; import com.moabam.api.dto.ModifyRoomRequest; +import com.moabam.support.annotation.WithMember; +import com.moabam.support.common.WithoutFilterSupporter; import com.moabam.support.fixture.BugFixture; import com.moabam.support.fixture.MemberFixture; @@ -49,7 +51,7 @@ @SpringBootTest @AutoConfigureMockMvc @TestInstance(TestInstance.Lifecycle.PER_CLASS) -class RoomControllerTest { +class RoomControllerTest extends WithoutFilterSupporter { @Autowired private MockMvc mockMvc; @@ -98,6 +100,7 @@ void cleanUp() { } @DisplayName("비밀번호 없는 방 생성 성공") + @WithMember @Test void create_room_no_password_success() throws Exception { // given diff --git a/src/test/java/com/moabam/global/common/handler/CurrentMemberArgumentResolverTest.java b/src/test/java/com/moabam/global/common/handler/CurrentMemberArgumentResolverTest.java new file mode 100644 index 00000000..99711dc7 --- /dev/null +++ b/src/test/java/com/moabam/global/common/handler/CurrentMemberArgumentResolverTest.java @@ -0,0 +1,115 @@ +package com.moabam.global.common.handler; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.BDDMockito.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.ModelAndViewContainer; + +import com.moabam.api.domain.entity.Member; +import com.moabam.api.domain.entity.enums.Role; +import com.moabam.api.dto.AuthorizationMember; +import com.moabam.global.common.annotation.CurrentMember; +import com.moabam.global.common.util.AuthorizationThreadLocal; + +@ExtendWith(MockitoExtension.class) +class CurrentMemberArgumentResolverTest { + + @InjectMocks + CurrentMemberArgumentResolver currentMemberArgumentResolver; + + @Nested + @DisplayName("제공 파라미터 검증") + class SupportParameter { + + @DisplayName("파라미터 제공 성공") + @Test + void support_parameter_success() { + // given + MethodParameter parameter = mock(MethodParameter.class); + + willReturn(mock(CurrentMember.class)) + .given(parameter).getParameterAnnotation(any()); + willReturn(AuthorizationMember.class) + .given(parameter).getParameterType(); + + // when + boolean support = currentMemberArgumentResolver.supportsParameter(parameter); + + // then + assertThat(support).isTrue(); + } + + @DisplayName("어노테이션이 없어서 지원 실패") + @Test + void support_paramter_failby_no_annotation() { + // given + MethodParameter parameter = mock(MethodParameter.class); + + willReturn(null) + .given(parameter).getParameterAnnotation(any()); + + // when + boolean support = currentMemberArgumentResolver.supportsParameter(parameter); + + // then + assertThat(support).isFalse(); + } + + @DisplayName("AuthorizationMember 클래스로 받지 않았을 때 실패") + @Test + void support_paramter_failby_not_authorizationmember() { + // given + MethodParameter parameter = mock(MethodParameter.class); + + willReturn(mock(CurrentMember.class)) + .given(parameter).getParameterAnnotation(any()); + willReturn(Member.class) + .given(parameter).getParameterType(); + + // when + boolean support = currentMemberArgumentResolver.supportsParameter(parameter); + + // then + assertThat(support).isFalse(); + } + } + + @DisplayName("값 변환한다") + @Nested + class Resolve { + + @DisplayName("값 변환 성공") + @Test + void resolve_argument_success() { + MethodParameter parameter = mock(MethodParameter.class); + ModelAndViewContainer mavContainer = mock(ModelAndViewContainer.class); + NativeWebRequest webRequest = mock(NativeWebRequest.class); + WebDataBinderFactory binderFactory = mock(WebDataBinderFactory.class); + + AuthorizationThreadLocal.setAuthorizationMember(new AuthorizationMember(1L, "park", Role.USER)); + + Object object = + currentMemberArgumentResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); + + assertAll( + () -> assertThat(object).isNotNull(), + () -> { + AuthorizationMember authorizationMember = (AuthorizationMember)object; + + assertThat(authorizationMember.id()).isEqualTo(1L); + } + ); + } + } + +} diff --git a/src/test/java/com/moabam/global/common/handler/PathResolverTest.java b/src/test/java/com/moabam/global/common/handler/PathResolverTest.java new file mode 100644 index 00000000..f77cd436 --- /dev/null +++ b/src/test/java/com/moabam/global/common/handler/PathResolverTest.java @@ -0,0 +1,65 @@ +package com.moabam.global.common.handler; + +import static com.moabam.api.domain.entity.enums.Role.*; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.http.HttpMethod.*; + +import java.util.List; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import com.moabam.global.common.handler.PathResolver; + +class PathResolverTest { + + @DisplayName("path 기본 생성 성공") + @Test + void create_basic_path_success() { + // given + PathResolver.Path path = PathResolver.Path.builder() + .uri("/") + .build(); + + assertAll( + () -> assertThat(path.uri()).isEqualTo("/"), + () -> assertThat(path.roles()).contains(USER), + () -> assertThat(path.httpMethods()).contains(GET, PUT, DELETE, POST, PATCH) + ); + } + + @DisplayName("method직접 설정 생성 성공") + @Test + void create_custom_mehtod_path_success() { + // given + PathResolver.Path path = PathResolver.Path.builder() + .uri("/") + .httpMethod(GET) + .httpMethods(List.of(POST, DELETE)) + .build(); + + assertAll( + () -> assertThat(path.uri()).isEqualTo("/"), + () -> assertThat(path.roles()).contains(USER), + () -> assertThat(path.httpMethods()).contains(GET, DELETE, POST) + ); + } + + @DisplayName("role직접 설정 생성 성공") + @Test + void create_role_mehtod_path_success() { + // given + PathResolver.Path path = PathResolver.Path.builder() + .uri("/") + .role(USER) + .roles(List.of(BLACK)) + .build(); + + assertAll( + () -> assertThat(path.uri()).isEqualTo("/"), + () -> assertThat(path.roles()).contains(USER, BLACK), + () -> assertThat(path.httpMethods()).contains(GET, PUT, DELETE, POST, PATCH) + ); + } +} diff --git a/src/test/java/com/moabam/global/filter/AuthorizationFilterTest.java b/src/test/java/com/moabam/global/filter/AuthorizationFilterTest.java new file mode 100644 index 00000000..0e762df5 --- /dev/null +++ b/src/test/java/com/moabam/global/filter/AuthorizationFilterTest.java @@ -0,0 +1,174 @@ +package com.moabam.global.filter; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.io.IOException; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.web.servlet.HandlerExceptionResolver; + +import com.moabam.api.application.AuthorizationService; +import com.moabam.api.application.JwtAuthenticationService; +import com.moabam.api.application.JwtProviderService; +import com.moabam.api.dto.AuthorizationMember; +import com.moabam.api.dto.PublicClaim; +import com.moabam.global.common.util.AuthorizationThreadLocal; +import com.moabam.global.error.exception.UnauthorizedException; +import com.moabam.support.fixture.JwtProviderFixture; +import com.moabam.support.fixture.PublicClaimFixture; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; + +@ExtendWith(MockitoExtension.class) +class AuthorizationFilterTest { + + @InjectMocks + AuthorizationFilter authorizationFilter; + + @Mock + HandlerExceptionResolver handlerExceptionResolver; + + @Mock + JwtAuthenticationService jwtAuthenticationService; + + @Mock + AuthorizationService authorizationService; + + @DisplayName("토큰 타입이 Bearer가 아니면 예외 발생") + @ParameterizedTest + @ValueSource(strings = { + "Access", "ID", "Self-signed", "Refresh", "Federated" + }) + void filter_token_type_mismatch(String tokenType) throws ServletException, IOException { + // Given + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + httpServletRequest.addHeader("token_type", tokenType); + MockFilterChain mockFilterChain = new MockFilterChain(); + + // When + Then + authorizationFilter.doFilter(httpServletRequest, httpServletResponse, mockFilterChain); + + verify(handlerExceptionResolver, times(1)) + .resolveException( + eq(httpServletRequest), eq(httpServletResponse), + eq(null), any(UnauthorizedException.class)); + } + + @DisplayName("필터가 쿠키가 없다면 예외 발생") + @Test + void filter_have_any_cookie_error() throws ServletException, IOException { + // Given + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + MockFilterChain mockFilterChain = new MockFilterChain(); + httpServletRequest.addHeader("token_type", "Bearer"); + + // when + authorizationFilter.doFilter(httpServletRequest, httpServletResponse, mockFilterChain); + + // then + verify(handlerExceptionResolver, times(1)) + .resolveException( + eq(httpServletRequest), eq(httpServletResponse), + eq(null), any(UnauthorizedException.class)); + } + + @DisplayName("엑세스 토큰이 없어서 예외 발생") + @Test + void filter_have_any_access_token_error() throws ServletException, IOException { + // given + JwtProviderService jwtProviderService = JwtProviderFixture.jwtProviderService(); + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + MockFilterChain mockFilterChain = new MockFilterChain(); + httpServletRequest.addHeader("token_type", "Bearer"); + + // when + String token = jwtProviderService.provideRefreshToken(); + httpServletRequest.setCookies(new Cookie("refresh_token", token)); + + authorizationFilter.doFilter(httpServletRequest, httpServletResponse, mockFilterChain); + + // then + verify(handlerExceptionResolver, times(1)) + .resolveException( + eq(httpServletRequest), eq(httpServletResponse), + eq(null), any(UnauthorizedException.class)); + } + + @DisplayName("refresh 토큰이 없어서 예외 발생") + @Test + void filter_have_any_refresh_token_error() throws ServletException, IOException { + // given + JwtProviderService jwtProviderService = JwtProviderFixture.jwtProviderService(); + PublicClaim publicClaim = PublicClaimFixture.publicClaim(); + + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + MockFilterChain mockFilterChain = new MockFilterChain(); + + // when + String token = jwtProviderService.provideAccessToken(publicClaim); + httpServletRequest.setCookies( + new Cookie("token_type", "Bearer"), + new Cookie("access_token", token)); + + when(jwtAuthenticationService.parseClaim(token)).thenReturn(publicClaim); + when(jwtAuthenticationService.isTokenExpire(token)).thenReturn(true); + + authorizationFilter.doFilter(httpServletRequest, httpServletResponse, mockFilterChain); + + // then + verify(handlerExceptionResolver, times(1)) + .resolveException( + eq(httpServletRequest), eq(httpServletResponse), + eq(null), any(UnauthorizedException.class)); + } + + @DisplayName("새로운 도큰 발급 성공") + @Test + void issue_new_token_success() throws ServletException, IOException { + // given + JwtProviderService jwtProviderService = JwtProviderFixture.jwtProviderService(); + PublicClaim publicClaim = PublicClaimFixture.publicClaim(); + + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + MockFilterChain mockFilterChain = new MockFilterChain(); + + // when + String accessToken = jwtProviderService.provideAccessToken(publicClaim); + String refreshToken = jwtProviderService.provideRefreshToken(); + httpServletRequest.setCookies( + new Cookie("token_type", "Bearer"), + new Cookie("access_token", accessToken), + new Cookie("refresh_token", refreshToken)); + + when(jwtAuthenticationService.parseClaim(accessToken)).thenReturn(publicClaim); + when(jwtAuthenticationService.isTokenExpire(accessToken)).thenReturn(true); + when(jwtAuthenticationService.isTokenExpire(refreshToken)).thenReturn(false); + + authorizationFilter.doFilter(httpServletRequest, httpServletResponse, mockFilterChain); + + // then + verify(authorizationService, times(1)) + .issueServiceToken(httpServletResponse, publicClaim); + + AuthorizationMember authorizationMember = AuthorizationThreadLocal.getAuthorizationMember(); + assertThat(authorizationMember.id()).isEqualTo(1L); + } + +} diff --git a/src/test/java/com/moabam/global/filter/PathFilterTest.java b/src/test/java/com/moabam/global/filter/PathFilterTest.java new file mode 100644 index 00000000..3429bb0a --- /dev/null +++ b/src/test/java/com/moabam/global/filter/PathFilterTest.java @@ -0,0 +1,76 @@ +package com.moabam.global.filter; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.*; + +import java.io.IOException; +import java.util.Optional; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +import com.moabam.global.common.handler.PathResolver; + +import jakarta.servlet.ServletException; + +@ExtendWith(MockitoExtension.class) +class PathFilterTest { + + @InjectMocks + PathFilter pathFilter; + + @Mock + PathResolver pathResolver; + + @DisplayName("Authentication을 넘기기 위한 필터 설정") + @ParameterizedTest + @ValueSource(strings = { + "GET", "POST", "PATCH", "DELETE" + }) + void filter_pass_for_authentication(String method) throws ServletException, IOException { + // given + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + httpServletRequest.setMethod(method); + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + + willReturn(Optional.of(PathResolver.Path.builder() + .uri("/") + .build())) + .given(pathResolver).permitPathMatch(any()); + + // when + pathFilter.doFilterInternal(httpServletRequest, httpServletResponse, new MockFilterChain()); + + // then + assertThat(httpServletRequest.getAttribute("isPermit")) + .isEqualTo(true); + } + + @DisplayName("경로 허가 없다.") + @Test + void filter_with_no_permit() throws ServletException, IOException { + // given + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + MockHttpServletResponse httpServletResponse = new MockHttpServletResponse(); + + willReturn(Optional.empty()) + .given(pathResolver) + .permitPathMatch(any()); + + // when + pathFilter.doFilterInternal(httpServletRequest, httpServletResponse, new MockFilterChain()); + + // then + assertThat(httpServletRequest.getAttribute("isPermit")).isNull(); + } +} diff --git a/src/test/java/com/moabam/support/annotation/WithMember.java b/src/test/java/com/moabam/support/annotation/WithMember.java new file mode 100644 index 00000000..8cfef2f1 --- /dev/null +++ b/src/test/java/com/moabam/support/annotation/WithMember.java @@ -0,0 +1,19 @@ +package com.moabam.support.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.moabam.api.domain.entity.enums.Role; + +@Target({ElementType.METHOD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface WithMember { + + long id() default 1L; + + String nickname() default "닉네임"; + + Role role() default Role.USER; +} diff --git a/src/test/java/com/moabam/support/common/FilterProcessExtension.java b/src/test/java/com/moabam/support/common/FilterProcessExtension.java new file mode 100644 index 00000000..391007dc --- /dev/null +++ b/src/test/java/com/moabam/support/common/FilterProcessExtension.java @@ -0,0 +1,61 @@ +package com.moabam.support.common; + +import static java.util.Objects.*; + +import java.lang.reflect.AnnotatedElement; + +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; + +import com.moabam.api.dto.AuthorizationMember; +import com.moabam.global.common.util.AuthorizationThreadLocal; +import com.moabam.support.annotation.WithMember; + +public class FilterProcessExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver { + + @Override + public void beforeEach(ExtensionContext context) { + AnnotatedElement annotatedElement = + context.getElement().orElse(null); + + if (isNull(annotatedElement)) { + return; + } + + WithMember withMember = annotatedElement.getAnnotation(WithMember.class); + + if (isNull(withMember)) { + return; + } + + AuthorizationThreadLocal.setAuthorizationMember( + new AuthorizationMember(withMember.id(), withMember.nickname(), withMember.role())); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + AuthorizationThreadLocal.remove(); + } + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws + ParameterResolutionException { + return parameterContext.getParameter().isAnnotationPresent(WithMember.class); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws + ParameterResolutionException { + WithMember withMember = parameterContext.getParameter().getAnnotation(WithMember.class); + + if (isNull(withMember)) { + return null; + } + + return new AuthorizationMember(withMember.id(), withMember.nickname(), withMember.role()); + } +} diff --git a/src/test/java/com/moabam/support/common/RestDocsFactory.java b/src/test/java/com/moabam/support/common/RestDocsFactory.java new file mode 100644 index 00000000..736f15f9 --- /dev/null +++ b/src/test/java/com/moabam/support/common/RestDocsFactory.java @@ -0,0 +1,31 @@ +package com.moabam.support.common; + +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; + +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.mockmvc.MockMvcSnippetConfigurer; +import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor; +import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor; + +public class RestDocsFactory { + + public static MockMvcSnippetConfigurer restdocs(RestDocumentationContextProvider restDocumentationContextProvider) { + return MockMvcRestDocumentation.documentationConfiguration(restDocumentationContextProvider) + .uris() + .withScheme("http") + .withHost("dev-api.moabam.com") + .withPort(80) + .and() + .snippets() + .withEncoding("UTF-8"); + } + + public static OperationRequestPreprocessor getDocumentRequest() { + return preprocessRequest(prettyPrint()); + } + + public static OperationResponsePreprocessor getDocumentResponse() { + return preprocessResponse(prettyPrint()); + } +} diff --git a/src/test/java/com/moabam/support/common/WithFilterSupporter.java b/src/test/java/com/moabam/support/common/WithFilterSupporter.java new file mode 100644 index 00000000..c5e198ca --- /dev/null +++ b/src/test/java/com/moabam/support/common/WithFilterSupporter.java @@ -0,0 +1,51 @@ +package com.moabam.support.common; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.restdocs.RestDocumentationContextProvider; +import org.springframework.restdocs.RestDocumentationExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import com.moabam.api.application.JwtProviderService; +import com.moabam.global.common.util.CookieUtils; +import com.moabam.global.config.TokenConfig; +import com.moabam.support.fixture.PublicClaimFixture; + +@SpringBootTest +public class WithFilterSupporter { + + @RegisterExtension + RestDocumentationExtension restDocumentationExtension = new RestDocumentationExtension(); + + @Autowired + WebApplicationContext webApplicationContext; + + @Autowired + JwtProviderService jwtProviderService; + + @Autowired + TokenConfig tokenConfig; + + protected MockMvc mockMvc; + + @BeforeEach + void setUpMockMvc(RestDocumentationContextProvider contextProvider) { + mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext) + .apply(RestDocsFactory.restdocs(contextProvider)) + .defaultRequest(get("/") + .cookie(CookieUtils.typeCookie("Bearer", tokenConfig.getRefreshExpire())) + .cookie(CookieUtils.tokenCookie("access_token", + jwtProviderService.provideAccessToken(PublicClaimFixture.publicClaim()), + tokenConfig.getRefreshExpire())) + .cookie(CookieUtils.tokenCookie("refresh_token", + jwtProviderService.provideRefreshToken(), + tokenConfig.getRefreshExpire()))) + .build(); + } +} diff --git a/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java new file mode 100644 index 00000000..189e6536 --- /dev/null +++ b/src/test/java/com/moabam/support/common/WithoutFilterSupporter.java @@ -0,0 +1,37 @@ +package com.moabam.support.common; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.BDDMockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.mock.mockito.MockBean; + +import com.moabam.api.application.AuthorizationService; +import com.moabam.api.application.JwtAuthenticationService; +import com.moabam.api.domain.entity.enums.Role; +import com.moabam.global.common.handler.PathResolver; + +@ExtendWith({FilterProcessExtension.class}) +public class WithoutFilterSupporter { + + @MockBean + private JwtAuthenticationService authenticationService; + + @MockBean + private AuthorizationService authorizationService; + + @MockBean + private PathResolver pathResolver; + + @BeforeEach + void setUpMock() { + willReturn(Optional.of(PathResolver.Path.builder() + .uri("/") + .role(Role.USER) + .build())) + .given(pathResolver).permitPathMatch(any()); + } +} diff --git a/src/test/java/com/moabam/support/fixture/JwtProviderFixture.java b/src/test/java/com/moabam/support/fixture/JwtProviderFixture.java new file mode 100644 index 00000000..951459aa --- /dev/null +++ b/src/test/java/com/moabam/support/fixture/JwtProviderFixture.java @@ -0,0 +1,19 @@ +package com.moabam.support.fixture; + +import com.moabam.api.application.JwtProviderService; +import com.moabam.global.config.TokenConfig; + +public class JwtProviderFixture { + + public static final String originIss = "PARK"; + public static final String originSecretKey = "testestestestestestestestestesttestestestestestestestestestest"; + public static final long originId = 1L; + public static final long originAccessExpire = 100000; + public static final long originRefreshExpire = 150000; + + public static JwtProviderService jwtProviderService() { + TokenConfig tokenConfig = new TokenConfig(originIss, originAccessExpire, originRefreshExpire, originSecretKey); + + return new JwtProviderService(tokenConfig); + } +} diff --git a/src/test/java/com/moabam/support/fixture/PublicClaimFixture.java b/src/test/java/com/moabam/support/fixture/PublicClaimFixture.java new file mode 100644 index 00000000..9e6d2e08 --- /dev/null +++ b/src/test/java/com/moabam/support/fixture/PublicClaimFixture.java @@ -0,0 +1,15 @@ +package com.moabam.support.fixture; + +import com.moabam.api.domain.entity.enums.Role; +import com.moabam.api.dto.PublicClaim; + +public class PublicClaimFixture { + + public static final PublicClaim publicClaim() { + return PublicClaim.builder() + .id(1L) + .nickname("nickname") + .role(Role.USER) + .build(); + } +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 723cc57c..d49e1d93 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -1,6 +1,7 @@ logging: level: org.hibernate.SQL: debug + org.springframework: DEBUG spring: