From 37bce49babf60c044ca7711f94dd7c0d3c9f7a15 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Wed, 10 Jul 2024 14:39:26 +0900 Subject: [PATCH 01/38] =?UTF-8?q?build=20:=20security=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/build.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/application/build.gradle.kts b/application/build.gradle.kts index 165b43db..9023dc66 100644 --- a/application/build.gradle.kts +++ b/application/build.gradle.kts @@ -10,6 +10,9 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework:spring-aspects") + // security + implementation("org.springframework.boot:spring-boot-starter-security") + // swagger implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:_") } From b3db4db84d8f198e974f20a2bd12d86d437a464f Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Wed, 10 Jul 2024 16:39:01 +0900 Subject: [PATCH 02/38] =?UTF-8?q?feat=20:=20security=20config=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/SecurityConfig.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java diff --git a/application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java b/application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java new file mode 100644 index 00000000..6747c3dd --- /dev/null +++ b/application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java @@ -0,0 +1,28 @@ +package org.depromeet.spot.application.common.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +public class SecurityConfig { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + // cross-site -> stateless라서 필요 없음. + .csrf(AbstractHttpConfigurer::disable) + // 초기 로그인 화면 필요 없음. + .formLogin(AbstractHttpConfigurer::disable) + // 토큰 방식을 사용하므로 httpBasic도 제거. + .httpBasic(AbstractHttpConfigurer::disable) + .authorizeHttpRequests( + authorize -> authorize + // 테스트, 개발 중엔 모든 경로 오픈. + .requestMatchers("/**").permitAll() + ); + return http.build(); + } +} From 2cd496c88233b028cd6909261daeb5fd0cc9c135 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 06:02:57 +0900 Subject: [PATCH 03/38] =?UTF-8?q?docs=20:=20jwt=20=EC=8B=9C=ED=81=AC?= =?UTF-8?q?=EB=A6=BF=20=ED=82=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/src/main/resources/application.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/src/main/resources/application.yaml b/application/src/main/resources/application.yaml index 1077c07f..fc088557 100644 --- a/application/src/main/resources/application.yaml +++ b/application/src/main/resources/application.yaml @@ -9,4 +9,6 @@ spring: # swagger를 이용해 API 명세서 생성 doc: swagger-ui: - path: /swagger-ui.html \ No newline at end of file + path: /swagger-ui.html + jwt: + secret: ${JWT_SECRETKEY} \ No newline at end of file From 279afa501fa80cac51aec38e9a2d93afbf677821 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 06:03:31 +0900 Subject: [PATCH 04/38] =?UTF-8?q?build=20:=20jwt=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=EB=9D=BC=EC=9D=B4=EB=B8=8C?= =?UTF-8?q?=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/build.gradle.kts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/application/build.gradle.kts b/application/build.gradle.kts index 9023dc66..aa50492f 100644 --- a/application/build.gradle.kts +++ b/application/build.gradle.kts @@ -15,6 +15,13 @@ dependencies { // swagger implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:_") + + // jwt +// implementation("io.jsonwebtoken:jjwt:_") + implementation("io.jsonwebtoken:jjwt-api:0.11.5") + implementation("io.jsonwebtoken:jjwt-impl:0.11.5") + implementation("io.jsonwebtoken:jjwt-jackson:0.11.5") + } // spring boot main application이므로 실행 가능한 jar를 생성한다. From 8cb0cd5b77f5ad4390f90ea3d0beb355f60c5467 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 06:03:57 +0900 Subject: [PATCH 05/38] =?UTF-8?q?feat=20:=20jwtFilter=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/common/config/SecurityConfig.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java b/application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java index 6747c3dd..ef4cd60d 100644 --- a/application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java +++ b/application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java @@ -1,14 +1,23 @@ package org.depromeet.spot.application.common.config; +import lombok.RequiredArgsConstructor; +import org.depromeet.spot.application.common.jwt.JwtAuthenticationFilter; +import org.depromeet.spot.application.common.jwt.JwtTokenUtil; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration +@EnableWebSecurity +@RequiredArgsConstructor public class SecurityConfig { + private final JwtTokenUtil jwtTokenUtil; + @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http @@ -22,7 +31,10 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { authorize -> authorize // 테스트, 개발 중엔 모든 경로 오픈. .requestMatchers("/**").permitAll() - ); + ) + // UsernamePasswordAuthenticationFilter 필터 전에 jwt 필터가 먼저 동작하도록함. + .addFilterBefore(new JwtAuthenticationFilter(jwtTokenUtil), UsernamePasswordAuthenticationFilter.class); return http.build(); } + } From 76aedced1af912ec49ed5959e7ef0e6d58deedf0 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 06:05:17 +0900 Subject: [PATCH 06/38] =?UTF-8?q?feat=20:=20jwt=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/common/jwt/JwtTokenUtil.java | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 application/src/main/java/org/depromeet/spot/application/common/jwt/JwtTokenUtil.java diff --git a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtTokenUtil.java b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtTokenUtil.java new file mode 100644 index 00000000..72c75467 --- /dev/null +++ b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtTokenUtil.java @@ -0,0 +1,117 @@ +package org.depromeet.spot.application.common.jwt; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.UnsupportedJwtException; +import io.jsonwebtoken.security.WeakKeyException; +import jakarta.annotation.PostConstruct; +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.util.Base64; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import javax.crypto.spec.SecretKeySpec; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.depromeet.spot.domain.member.MemberRole; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class JwtTokenUtil { + // JWT를 생성하고 관리하는 클래스 + + // 토큰에 사용되는 시크릿 키 + @Value("${spring.jwt.secret}") + private String secretKey; + + // 객체 초기화, secretKey를 Base64로 인코딩 + @PostConstruct + protected void init() { + secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes()); + } + + public String generateToken(Long memberId, MemberRole memberRole){ + return Jwts.builder() + .setHeader(createHeader()) + .setClaims(createClaims(memberRole)) + .setSubject(memberId.toString()) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis() + 1000*60)) // 토큰 만료 시간 + .signWith(SignatureAlgorithm.HS256, secretKey) + .compact(); + } + + public String getIdFromJWT(String token) { + Claims claims = Jwts.parser() + .setSigningKey(secretKey) + .parseClaimsJws(token) + .getBody(); + + return claims.get("id", String.class); + } + + public String getRoleFromJWT(String token) { + Claims claims = Jwts.parser() + .setSigningKey(secretKey) + .parseClaimsJws(token) + .getBody(); + + return claims.get("role", String.class); + } + + public Jws getClaims(String token){ + return Jwts.parserBuilder() + .setSigningKey(createSignature()) + .build() + .parseClaimsJws(token); + } + + public boolean isValidToken(String token) { + try { + Jws claims = getClaims(token); + return true; + } catch (ExpiredJwtException exception) { + log.error("Token Expired"); + throw new ExpiredJwtException(exception.getHeader(), exception.getClaims(),token); + } catch (UnsupportedJwtException | WeakKeyException exception) { + log.error("Unsupported Token"); + throw new UnsupportedJwtException("지원되지 않는 토큰입니다."); + } catch (MalformedJwtException | IllegalArgumentException exception){ + throw new MalformedJwtException("잘못된 형식의 토큰입니다."); + } + } + + + + private Map createHeader(){ + // 헤더 생성 + Map headers = new HashMap<>(); + + headers.put("typ", "JWT"); + headers.put("alg", "HS256"); // 서명? 생성에 사용될 알고리즘 + + return headers; + } + + // Claim -> 정보를 key-value 형태로 저장함. + private Map createClaims(MemberRole role){ + Map claims = new HashMap<>(); + + claims.put("role", role); + return claims; + } + + private Key createSignature() { + byte[] apiKeySecretBytes = secretKey.getBytes(StandardCharsets.UTF_8); + return new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS256.getJcaName()); + } + +} From 7ba69139090cd235b74e7d12efc07269d9a8cf70 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 06:05:27 +0900 Subject: [PATCH 07/38] =?UTF-8?q?feat=20:=20jwtFilter=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/jwt/JwtAuthenticationFilter.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java diff --git a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java new file mode 100644 index 00000000..e0a8bc6b --- /dev/null +++ b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java @@ -0,0 +1,58 @@ +package org.depromeet.spot.application.common.jwt; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.server.ResponseStatusException; + +@RequiredArgsConstructor +@Slf4j +@Component +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtTokenUtil jwtTokenUtil; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + + List list = List.of( + // swagger, 회원가입은 제외 + "/swagger-ui/", + "/swagger-resources/", + "/api/v1/" + ); + boolean flag = list.stream().anyMatch(url -> request.getRequestURI().startsWith(url)); + // 현재 URL 이 LIST 안에 포함되있는걸로 시작하는가? + if(flag) { + filterChain.doFilter(request,response); + return; + } + + String header = request.getHeader(HttpHeaders.AUTHORIZATION); + log.info("JwtAuthenticationFilter header : {}", header); + // header가 null이거나 빈 문자열이면 안됨. + if(header != null && !header.equalsIgnoreCase("")){ + if(header.startsWith("Bearer")){ + String access_token = header.split(" ")[1]; + if(jwtTokenUtil.isValidToken(access_token)){ + String memberId = jwtTokenUtil.getIdFromJWT(header); + String role = jwtTokenUtil.getRoleFromJWT(header); + filterChain.doFilter(request,response); + } + } + // 토큰 검증 실패 -> Exception + } else throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + } + + +} From ed3a20612ac62fca2bb89f670fc126637b0bf127 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 06:07:14 +0900 Subject: [PATCH 08/38] =?UTF-8?q?docs=20:=20jwt=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- versions.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/versions.properties b/versions.properties index 5844e6f2..eef11218 100644 --- a/versions.properties +++ b/versions.properties @@ -13,6 +13,8 @@ plugin.io.spring.dependency-management=1.0.11.RELEASE plugin.com.diffplug.spotless=6.21.0 +version.io.jsonwebtoken..jjwt=0.12.6 + version.junit=5.9.1 version.org.projectlombok..lombok=1.18.30 @@ -28,4 +30,3 @@ version.com.github.gavlyukovskiy..p6spy-spring-boot-starter=1.9.0 version.com.querydsl..querydsl-apt=5.0.0 version.com.querydsl..querydsl-jpa=5.0.0 - From 20888cd0b3cfd2ff7e03ca1d2dd4c7c7535ac84d Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 07:02:49 +0900 Subject: [PATCH 09/38] =?UTF-8?q?docs=20:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?Http=20=EC=9A=94=EC=B2=AD=EC=9D=84=20=EC=9C=84=ED=95=9C=20webfl?= =?UTF-8?q?ux=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- infrastructure/jpa/build.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/infrastructure/jpa/build.gradle.kts b/infrastructure/jpa/build.gradle.kts index af4e9e27..52d7e398 100644 --- a/infrastructure/jpa/build.gradle.kts +++ b/infrastructure/jpa/build.gradle.kts @@ -16,6 +16,9 @@ dependencies { // p6spy implementation("com.github.gavlyukovskiy:p6spy-spring-boot-starter:_") + + // webflux (HTTP 요청에 사용) + implementation("org.springframework.boot:spring-boot-starter-webflux") } tasks.bootJar { enabled = false } From 52685e208ef0672c8260e77c731225ed742b0379 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 07:03:22 +0900 Subject: [PATCH 10/38] =?UTF-8?q?feat=20:=20member=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../depromeet/spot/domain/member/Member.java | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/domain/src/main/java/org/depromeet/spot/domain/member/Member.java b/domain/src/main/java/org/depromeet/spot/domain/member/Member.java index 7c141f54..9ad7425b 100644 --- a/domain/src/main/java/org/depromeet/spot/domain/member/Member.java +++ b/domain/src/main/java/org/depromeet/spot/domain/member/Member.java @@ -1,15 +1,55 @@ package org.depromeet.spot.domain.member; +import lombok.Builder; import lombok.Getter; +import org.depromeet.spot.domain.member.enums.MemberRole; +import org.depromeet.spot.domain.member.enums.SnsProvider; +import java.time.LocalDateTime; @Getter +@Builder public class Member { - private final Long id; + private final Long userId; + private final String email; private final String name; + private final String nickname; + private final String phoneNumber; + private final Integer level; + private final String profileImage; + private final SnsProvider snsProvider; + private final String idToken; + private final Long teamId; + private final MemberRole role; + private final LocalDateTime createdAt; + private final LocalDateTime deletedAt; - public Member(Long id, String name) { - this.id = id; + public Member( + Long userId, + String email, + String name, + String nickname, + String phoneNumber, + Integer level, + String profileImage, + SnsProvider snsProvider, + String idToken, + Long teamId, + MemberRole role, + LocalDateTime createdAt, + LocalDateTime deletedAt) { + this.userId = userId; + this.email = email; this.name = name; + this.nickname = nickname; + this.phoneNumber = phoneNumber; + this.level = level; + this.profileImage = profileImage; + this.snsProvider = snsProvider; + this.idToken = idToken; + this.teamId = teamId; + this.role = role; + this.createdAt = createdAt; + this.deletedAt = deletedAt; } -} +} \ No newline at end of file From 4e3ff59cad0022d45094bddce3f07909b0ca26ed Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 07:04:19 +0900 Subject: [PATCH 11/38] =?UTF-8?q?feat=20:=20memberEntity=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/jpa/member/entity/MemberEntity.java | 81 ++++++++++++++----- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java index f8d00adf..1edd14be 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java @@ -1,41 +1,80 @@ package org.depromeet.spot.jpa.member.entity; -/* JPA 설정 확인용 샘플 엔티티. 실제 피처 개발 시작할 때 삭제 예정! */ - import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; import jakarta.persistence.Table; - +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; import org.depromeet.spot.domain.member.Member; +import org.depromeet.spot.domain.member.enums.MemberRole; +import org.depromeet.spot.domain.member.enums.SnsProvider; +import org.depromeet.spot.jpa.common.entity.BaseEntity; -import lombok.NoArgsConstructor; @Entity -@Table(name = "member") +@Table(name = "members") @NoArgsConstructor -public class MemberEntity { +@AllArgsConstructor +public class MemberEntity extends BaseEntity { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id", nullable = false) - private Long id; + @Column(name = "email", nullable = false, unique = true, length = 50) + private String email; - @Column(name = "name", nullable = false) + @Column(name = "name", nullable = false, length = 20) private String name; - public MemberEntity(Long id, String name) { - this.id = id; - this.name = name; - } + @Column(name = "nickname", nullable = false, unique = true, length = 10) + private String nickname; + + @Column(name = "phone_number", nullable = false, unique = true, length = 13) + private String phoneNumber; + + @Column(name = "level", nullable = false) + private Integer level; + + @Column(name = "profile_image", length = 255) + private String profileImage; + + @Column(name = "sns_provider", nullable = false, length = 20) + private String snsProvider; + + @Column(name = "id_token", nullable = false, unique = true, length = 255) + private String idToken; + + @Column(name = "team_id", nullable = false, length = 10) + private Long teamId; + + @Column(name = "role", nullable = false) + private String role; public static MemberEntity from(Member member) { - return new MemberEntity(member.getId(), member.getName()); + return new MemberEntity( + member.getEmail(), + member.getName(), + member.getNickname(), + member.getPhoneNumber(), + member.getLevel(), + member.getProfileImage(), + member.getSnsProvider().getValue(), + member.getIdToken(), + member.getTeamId(), + member.getRole().getValue()); } public Member toDomain() { - return new Member(id, name); + return new Member( + this.getId(), + email, + name, + nickname, + phoneNumber, + level, + profileImage, + SnsProvider.valueOf(snsProvider), + idToken, + teamId, + MemberRole.valueOf(role), + this.getCreatedAt(), + this.getDeletedAt()); } -} + } \ No newline at end of file From 37e56d931385028eefe8cfadcf67d54729ddd862 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 07:07:58 +0900 Subject: [PATCH 12/38] =?UTF-8?q?feat=20:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EC=97=91=EC=84=B8=EC=8A=A4=20=ED=86=A0=ED=81=B0,=20=EC=9C=A0?= =?UTF-8?q?=EC=A0=80=20=EC=A0=95=EB=B3=B4=20=EA=B0=80=EC=A0=B8=EC=98=A4?= =?UTF-8?q?=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/domain/member/enums/SnsProvider.java | 12 ++ .../spot/jpa/oauth/OauthRepositoryImpl.java | 82 +++++++++++++ .../jpa/oauth/entity/KakaoTokenEntity.java | 28 +++++ .../jpa/oauth/entity/KakaoUserInfoEntity.java | 113 ++++++++++++++++++ .../port/out/oauth/OauthRepository.java | 11 ++ 5 files changed, 246 insertions(+) create mode 100644 domain/src/main/java/org/depromeet/spot/domain/member/enums/SnsProvider.java create mode 100644 infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java create mode 100644 infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoTokenEntity.java create mode 100644 infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java create mode 100644 usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java diff --git a/domain/src/main/java/org/depromeet/spot/domain/member/enums/SnsProvider.java b/domain/src/main/java/org/depromeet/spot/domain/member/enums/SnsProvider.java new file mode 100644 index 00000000..f5cc1349 --- /dev/null +++ b/domain/src/main/java/org/depromeet/spot/domain/member/enums/SnsProvider.java @@ -0,0 +1,12 @@ +package org.depromeet.spot.domain.member.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum SnsProvider { + KAKAO("KAKAO"); + + private final String value; +} diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java new file mode 100644 index 00000000..6ee7b205 --- /dev/null +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java @@ -0,0 +1,82 @@ +package org.depromeet.spot.jpa.oauth; + +import io.netty.handler.codec.http.HttpHeaderValues; +import lombok.extern.slf4j.Slf4j; +import org.depromeet.spot.domain.member.Member; +import org.depromeet.spot.jpa.oauth.entity.KakaoTokenEntity; +import org.depromeet.spot.jpa.oauth.entity.KakaoUserInfoEntity; +import org.depromeet.spot.usecase.port.out.oauth.OauthRepository; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatusCode; +import org.springframework.stereotype.Repository; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +@Slf4j +@Repository +public class OauthRepositoryImpl implements OauthRepository { + + @Override + public String getKakaoAccessToken(String idCode) { + String KAUTH_TOKEN_URL_HOST ="https://kauth.kakao.com"; + + // kakao에서 발급 받은 clientID + String clientId = "1f043adfbb8f5438907686f472d3b164"; + + // Webflux의 WebClient + KakaoTokenEntity kakaoTokenEntity = WebClient.create(KAUTH_TOKEN_URL_HOST).post() + .uri(uriBuilder -> uriBuilder + .scheme("https") + .path("/oauth/token") + .queryParam("grant_type", "authorization_code") + .queryParam("client_id", clientId) + .queryParam("code", idCode) + .build(true)) + .header(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()) + .retrieve() + // TODO : Custom Exception + .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.error(new RuntimeException("Invalid Parameter"))) + .onStatus(HttpStatusCode::is5xxServerError, clientResponse -> Mono.error(new RuntimeException("Internal Server Error"))) + .bodyToMono(KakaoTokenEntity.class) + .block(); + + + log.info(" [Kakao Service] Access Token ------> {}", kakaoTokenEntity.getAccessToken()); + log.info(" [Kakao Service] Refresh Token ------> {}", kakaoTokenEntity.getRefreshToken()); +// //제공 조건: OpenID Connect가 활성화 된 앱의 토큰 발급 요청인 경우 또는 scope에 openid를 포함한 추가 항목 동의 받기 요청을 거친 토큰 발급 요청인 경우 +// log.info(" [Kakao Service] Id Token ------> {}", kakaoTokenResponseDto.getIdToken()); +// log.info(" [Kakao Service] Scope ------> {}", kakaoTokenResponseDto.getScope()); + + return kakaoTokenEntity.getAccessToken(); + } + + @Override + public Member getUserInfo(String accessToken) { + // 엑세스 토큰으로 카카오에서 유저 정보 받아오기 + String KAUTH_USER_URL_HOST = "https://kapi.kakao.com"; + + KakaoUserInfoEntity userInfo = WebClient.create(KAUTH_USER_URL_HOST) + .get() + .uri(uriBuilder -> uriBuilder + .scheme("https") + .path("/v2/user/me") + .build(true)) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) // access token 인가 + .header(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()) + .retrieve() + // TODO : Custom Exception + .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.error(new RuntimeException("Invalid Parameter"))) + .onStatus(HttpStatusCode::is5xxServerError, clientResponse -> Mono.error(new RuntimeException("Internal Server Error"))) + .bodyToMono(KakaoUserInfoEntity.class) + .block(); + + log.info("kakao AuthId : \n {} ", userInfo.getId()); + log.info("nickname : \n {} ", userInfo.getKakaoAccount().getProfile().getNickName()); + log.info("ProfileImageUrl : \n {} ", userInfo.getKakaoAccount().getProfile().getProfileImageUrl()); + log.info("kakao user info : \n {}", userInfo.toString()); + + // member로 변환해서 리턴. + return userInfo.toKakaoDomain(); + } +} + diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoTokenEntity.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoTokenEntity.java new file mode 100644 index 00000000..7af922fc --- /dev/null +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoTokenEntity.java @@ -0,0 +1,28 @@ +package org.depromeet.spot.jpa.oauth.entity; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.depromeet.spot.jpa.common.entity.BaseEntity; + +@Getter +@NoArgsConstructor // 역직렬화를 위한 기본 생성자 +@JsonIgnoreProperties(ignoreUnknown = true) +public class KakaoTokenEntity extends BaseEntity { + + @JsonProperty("token_type") + public String tokenType; + @JsonProperty("access_token") + public String accessToken; + @JsonProperty("id_token") + public String idToken; + @JsonProperty("expires_in") + public Integer expiresIn; + @JsonProperty("refresh_token") + public String refreshToken; + @JsonProperty("refresh_token_expires_in") + public Integer refreshTokenExpiresIn; + @JsonProperty("scope") + public String scope; +} \ No newline at end of file diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java new file mode 100644 index 00000000..7f6b4dbc --- /dev/null +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java @@ -0,0 +1,113 @@ +package org.depromeet.spot.jpa.oauth.entity; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.depromeet.spot.domain.member.Member; +import org.depromeet.spot.domain.member.enums.MemberRole; +import org.depromeet.spot.domain.member.enums.SnsProvider; +import org.depromeet.spot.jpa.common.entity.BaseEntity; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; + +@Getter +@NoArgsConstructor // 역직렬화를 위한 기본 생성자 +@JsonIgnoreProperties(ignoreUnknown = true) +public class KakaoUserInfoEntity extends BaseEntity { + + // 서비스에 연결 완료된 시각. UTC + @JsonProperty("connected_at") + public Date connectedAt; + + // 카카오 계정 정보 + @JsonProperty("kakao_account") + public KakaoAccount kakaoAccount; + + @Getter + @NoArgsConstructor + @ToString + @JsonIgnoreProperties(ignoreUnknown = true) + public class KakaoAccount { + + // 사용자 프로필 정보 + @JsonProperty("profile") + public Profile profile; + + // 이름 제공 동의 여부 + @JsonProperty("name_needs_agreement") + public Boolean isNameAgree; + + // 카카오계정 이름 + @JsonProperty("name") + public String name; + + // 이메일 제공 동의 여부 + @JsonProperty("email_needs_agreement") + public Boolean isEmailAgree; + + // 이메일이 유효 여부 + // true : 유효한 이메일, false : 이메일이 다른 카카오 계정에 사용돼 만료 + @JsonProperty("is_email_valid") + public Boolean isEmailValid; + + // 이메일이 인증 여부 + // true : 인증된 이메일, false : 인증되지 않은 이메일 + @JsonProperty("is_email_verified") + public Boolean isEmailVerified; + + // 카카오계정 대표 이메일 + @JsonProperty("email") + public String email; + + // 성별 + @JsonProperty("gender") + public String gender; + + // 전화번호 + // +82 00-0000-0000 형식 + @JsonProperty("phone_number") + public String phoneNumber; + + @Getter + @NoArgsConstructor + @ToString + @JsonIgnoreProperties(ignoreUnknown = true) + public class Profile { + + //닉네임 + @JsonProperty("nickname") + public String nickName; + + //프로필 미리보기 이미지 URL + @JsonProperty("thumbnail_image_url") + public String thumbnailImageUrl; + + //프로필 사진 URL + @JsonProperty("profile_image_url") + public String profileImageUrl; + + } + } + + public Member toKakaoDomain(){ + return Member.builder() + .email(kakaoAccount.email) + .name(kakaoAccount.name) + .nickname(kakaoAccount.getProfile().nickName) + .phoneNumber(kakaoAccount.phoneNumber) + .profileImage(kakaoAccount.profile.profileImageUrl) + .snsProvider(SnsProvider.KAKAO) + .idToken(id) + .role(MemberRole.USER) + .createdAt(toLocalDateTime(connectedAt)) + .build(); + } + + public LocalDateTime toLocalDateTime(Date date){ + return date.toInstant().atZone(ZoneId.of("Asia/Seoul")).toLocalDateTime(); + } + +} \ No newline at end of file diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java new file mode 100644 index 00000000..00f8f4fb --- /dev/null +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java @@ -0,0 +1,11 @@ +package org.depromeet.spot.usecase.port.out.oauth; + +import org.depromeet.spot.domain.member.Member; + +public interface OauthRepository { + + String getKakaoAccessToken(String idCode); + + Member getUserInfo(String accesstoken); + +} From b227d2cb107e8fd27c870875fefcec59472c8fd0 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 09:06:08 +0900 Subject: [PATCH 13/38] =?UTF-8?q?feat=20:=20=EB=A9=A4=EB=B2=84=20Role=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=EB=A5=BC=20=EC=9C=84=ED=95=9C=20enum=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/domain/member/enums/MemberRole.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 domain/src/main/java/org/depromeet/spot/domain/member/enums/MemberRole.java diff --git a/domain/src/main/java/org/depromeet/spot/domain/member/enums/MemberRole.java b/domain/src/main/java/org/depromeet/spot/domain/member/enums/MemberRole.java new file mode 100644 index 00000000..1f7c25c1 --- /dev/null +++ b/domain/src/main/java/org/depromeet/spot/domain/member/enums/MemberRole.java @@ -0,0 +1,13 @@ +package org.depromeet.spot.domain.member.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum MemberRole { + ROLE_USER("ROLE_USER"), + ROLE_ADMIN("ROLE_ADMIN"); + + private final String value; +} From a6696a0c46a3701843d46900967f5f0000a2e790 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 09:08:03 +0900 Subject: [PATCH 14/38] =?UTF-8?q?remove=20:=20=EC=B4=88=EA=B8=B0=20?= =?UTF-8?q?=EC=84=B8=ED=8C=85=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=9A=A9?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9E=91=EC=84=B1=EB=90=9C=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/MemberCustomRepository.java | 31 ----------------- .../spot/usecase/port/in/MemberUsecase.java | 12 ------- .../usecase/port/out/MemberRepository.java | 12 ------- .../spot/usecase/service/MemberService.java | 33 ------------------- 4 files changed, 88 deletions(-) delete mode 100644 infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberCustomRepository.java delete mode 100644 usecase/src/main/java/org/depromeet/spot/usecase/port/in/MemberUsecase.java delete mode 100644 usecase/src/main/java/org/depromeet/spot/usecase/port/out/MemberRepository.java delete mode 100644 usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberCustomRepository.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberCustomRepository.java deleted file mode 100644 index f0d490a7..00000000 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberCustomRepository.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.depromeet.spot.jpa.member.repository; - -import static org.depromeet.spot.jpa.member.entity.QMemberEntity.memberEntity; - -import java.util.List; - -import org.depromeet.spot.jpa.member.entity.MemberEntity; -import org.springframework.stereotype.Repository; - -import com.querydsl.core.types.dsl.BooleanExpression; -import com.querydsl.jpa.impl.JPAQueryFactory; - -import lombok.RequiredArgsConstructor; - -@Repository -@RequiredArgsConstructor -public class MemberCustomRepository { - - private final JPAQueryFactory queryFactory; - - public List findByName(final String name) { - return queryFactory.selectFrom(memberEntity).where(eqMemberName(name)).fetch(); - } - - private BooleanExpression eqMemberName(final String name) { - if (name == null) { - return null; - } - return memberEntity.name.eq(name); - } -} diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/in/MemberUsecase.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/in/MemberUsecase.java deleted file mode 100644 index 42b00e6e..00000000 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/in/MemberUsecase.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.depromeet.spot.usecase.port.in; - -import java.util.List; - -import org.depromeet.spot.domain.member.Member; - -public interface MemberUsecase { - - Member create(String name); - - List findByName(String name); -} diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/MemberRepository.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/MemberRepository.java deleted file mode 100644 index 58da7024..00000000 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/MemberRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.depromeet.spot.usecase.port.out; - -import java.util.List; - -import org.depromeet.spot.domain.member.Member; - -public interface MemberRepository { - - Member save(Member member); - - List findByName(String name); -} diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java deleted file mode 100644 index cba1b8d6..00000000 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.depromeet.spot.usecase.service; - -import java.util.List; - -import org.depromeet.spot.common.exception.member.MemberException.MemberNotFoundException; -import org.depromeet.spot.domain.member.Member; -import org.depromeet.spot.usecase.port.in.MemberUsecase; -import org.depromeet.spot.usecase.port.out.MemberRepository; -import org.springframework.stereotype.Service; - -import lombok.RequiredArgsConstructor; - -@Service -@RequiredArgsConstructor -public class MemberService implements MemberUsecase { - - private final MemberRepository memberRepository; - - @Override - public Member create(final String name) { - var member = new Member(null, name); - return memberRepository.save(member); - } - - @Override - public List findByName(final String name) { - var members = memberRepository.findByName(name); - if (members.isEmpty()) { - throw new MemberNotFoundException("name : " + name); - } - return members; - } -} From 1cec21bf9bacabb3b09aabf7d4caf01ed133c8c6 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 09:14:00 +0900 Subject: [PATCH 15/38] =?UTF-8?q?feat=20:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=ED=99=95=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 32 +++++++++---------- .../repository/MemberJpaRepository.java | 2 ++ .../repository/MemberRepositoryImpl.java | 16 ++++++---- .../port/out/member/MemberRepository.java | 12 +++++++ 4 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 usecase/src/main/java/org/depromeet/spot/usecase/port/out/member/MemberRepository.java diff --git a/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java b/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java index 673abfb3..a545d7b7 100644 --- a/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java +++ b/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java @@ -3,10 +3,15 @@ import java.util.List; import org.depromeet.spot.application.member.dto.request.MemberRequest; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.depromeet.spot.application.member.dto.response.MemberResponse; -import org.depromeet.spot.usecase.port.in.MemberUsecase; +import org.depromeet.spot.domain.member.Member; +import org.depromeet.spot.usecase.port.in.member.MemberUsecase; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -14,17 +19,11 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import lombok.val; - -// FIXME: JPA 확인용 샘플 컨트롤러 입니다. 이후 실제 작업 시작할 때 삭제 예정이에요! @RestController @RequiredArgsConstructor +@Slf4j @Tag(name = "멤버") -@RequestMapping("/api/members") +@RequestMapping("/api/v1/members") public class MemberController { private final MemberUsecase memberUsecase; @@ -37,13 +36,14 @@ public MemberResponse create(@RequestBody MemberRequest request) { return MemberResponse.from(member); } - @GetMapping + @GetMapping("/duplicatedNickname") @ResponseStatus(HttpStatus.OK) - @Operation(summary = "이름으로 Member 조회하는 API") - public List findByName( - @RequestParam("name") @Parameter(name = "name", description = "사용자 이름", required = true) - final String name) { - val memberList = memberUsecase.findByName(name); - return memberList.stream().map(MemberResponse::from).toList(); + @Operation(summary = "닉네임 중복확인 API") + public Boolean duplicatedNickname( + @RequestParam("nickname") + @Parameter(name = "nickname", description = "닉네임", required = true) + String nickname) { + Boolean result = memberUsecase.duplicatedNickname(nickname); + return result; } } diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberJpaRepository.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberJpaRepository.java index 502030df..a318fdc1 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberJpaRepository.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberJpaRepository.java @@ -4,3 +4,5 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface MemberJpaRepository extends JpaRepository {} + Boolean existsByNickname(String nickname); +} diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java index 8c4ebba9..f1d69261 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java @@ -1,21 +1,16 @@ package org.depromeet.spot.jpa.member.repository; -import java.util.List; - +import lombok.RequiredArgsConstructor; import org.depromeet.spot.domain.member.Member; import org.depromeet.spot.jpa.member.entity.MemberEntity; -import org.depromeet.spot.usecase.port.out.MemberRepository; +import org.depromeet.spot.usecase.port.out.member.MemberRepository; import org.springframework.stereotype.Repository; -import lombok.RequiredArgsConstructor; -import lombok.val; - @Repository @RequiredArgsConstructor public class MemberRepositoryImpl implements MemberRepository { private final MemberJpaRepository memberJpaRepository; - private final MemberCustomRepository memberCustomRepository; @Override public Member save(Member member) { @@ -28,4 +23,11 @@ public List findByName(String name) { val memberEntities = memberCustomRepository.findByName(name); return memberEntities.stream().map(MemberEntity::toDomain).toList(); } + + @Override + public Boolean existsByNickname(String nickname) { + return memberJpaRepository.existsByNickname(nickname); + } + + } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/member/MemberRepository.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/member/MemberRepository.java new file mode 100644 index 00000000..0258d12e --- /dev/null +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/member/MemberRepository.java @@ -0,0 +1,12 @@ +package org.depromeet.spot.usecase.port.out.member; + +import org.depromeet.spot.domain.member.Member; + +public interface MemberRepository { + + Member save(Member member); + + Member findByIdToken(String idToken); + + Boolean existsByNickname(String nickname); +} From 94e99f97380830a34772efc9477f522bcdf03450 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 09:14:19 +0900 Subject: [PATCH 16/38] =?UTF-8?q?feat=20:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=ED=99=95=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/member/controller/MemberController.java | 8 +++++--- .../spot/jpa/member/repository/MemberJpaRepository.java | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java b/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java index a545d7b7..3ad4cfdb 100644 --- a/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java +++ b/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java @@ -1,11 +1,13 @@ package org.depromeet.spot.application.member.controller; -import java.util.List; - -import org.depromeet.spot.application.member.dto.request.MemberRequest; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.depromeet.spot.application.common.jwt.JwtTokenUtil; +import org.depromeet.spot.application.member.dto.request.RegisterReq; import org.depromeet.spot.application.member.dto.response.MemberResponse; import org.depromeet.spot.domain.member.Member; import org.depromeet.spot.usecase.port.in.member.MemberUsecase; diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberJpaRepository.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberJpaRepository.java index a318fdc1..61d4e50e 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberJpaRepository.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberJpaRepository.java @@ -3,6 +3,8 @@ import org.depromeet.spot.jpa.member.entity.MemberEntity; import org.springframework.data.jpa.repository.JpaRepository; -public interface MemberJpaRepository extends JpaRepository {} +public interface MemberJpaRepository extends JpaRepository { + MemberEntity findByIdToken(String idToken); + Boolean existsByNickname(String nickname); } From 3b6cf1f193736403807a3afb750c386dd37aa81b Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 09:14:47 +0900 Subject: [PATCH 17/38] =?UTF-8?q?feat=20:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20=EC=97=90=EB=9F=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/common/exception/member/MemberErrorCode.java | 1 + .../spot/common/exception/member/MemberException.java | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/common/src/main/java/org/depromeet/spot/common/exception/member/MemberErrorCode.java b/common/src/main/java/org/depromeet/spot/common/exception/member/MemberErrorCode.java index be953a57..6f1ba273 100644 --- a/common/src/main/java/org/depromeet/spot/common/exception/member/MemberErrorCode.java +++ b/common/src/main/java/org/depromeet/spot/common/exception/member/MemberErrorCode.java @@ -8,6 +8,7 @@ @Getter public enum MemberErrorCode implements ErrorCode { MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "M001", "요청 유저가 존재하지 않습니다."), + MEMBER_NICKNAME_CONFLICT(HttpStatus.CONFLICT, "M002", "닉네임이 중복됩니다."), ; private final HttpStatus status; diff --git a/common/src/main/java/org/depromeet/spot/common/exception/member/MemberException.java b/common/src/main/java/org/depromeet/spot/common/exception/member/MemberException.java index adf73795..b3510004 100644 --- a/common/src/main/java/org/depromeet/spot/common/exception/member/MemberException.java +++ b/common/src/main/java/org/depromeet/spot/common/exception/member/MemberException.java @@ -17,4 +17,11 @@ public MemberNotFoundException(Object o) { super(MemberErrorCode.MEMBER_NOT_FOUND.appended(o)); } } + + public static class MemberNicknameConflictException extends MemberException { + public MemberNicknameConflictException() { + super(MemberErrorCode.MEMBER_NICKNAME_CONFLICT); + } + } + } From 781679e2d405de5fde4e1589dd155e4214e6ae3b Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 09:15:32 +0900 Subject: [PATCH 18/38] =?UTF-8?q?refactor=20:=20=EC=B9=B4=EC=B9=B4?= =?UTF-8?q?=EC=98=A4=20=EA=B3=84=EC=A0=95=20=EC=A0=95=EB=B3=B4=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=A4=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/jpa/oauth/OauthRepositoryImpl.java | 20 +++++++++---------- .../port/out/oauth/OauthRepository.java | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java index 6ee7b205..1323ef21 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java @@ -41,17 +41,17 @@ public String getKakaoAccessToken(String idCode) { .block(); - log.info(" [Kakao Service] Access Token ------> {}", kakaoTokenEntity.getAccessToken()); - log.info(" [Kakao Service] Refresh Token ------> {}", kakaoTokenEntity.getRefreshToken()); + log.info("Access Token : {}", kakaoTokenEntity.getAccessToken()); + log.info("Refresh Token : {}", kakaoTokenEntity.getRefreshToken()); // //제공 조건: OpenID Connect가 활성화 된 앱의 토큰 발급 요청인 경우 또는 scope에 openid를 포함한 추가 항목 동의 받기 요청을 거친 토큰 발급 요청인 경우 -// log.info(" [Kakao Service] Id Token ------> {}", kakaoTokenResponseDto.getIdToken()); -// log.info(" [Kakao Service] Scope ------> {}", kakaoTokenResponseDto.getScope()); +// log.info("Id Token : {}", kakaoTokenResponseDto.getIdToken()); +// log.info("Scope : {}", kakaoTokenResponseDto.getScope()); return kakaoTokenEntity.getAccessToken(); } @Override - public Member getUserInfo(String accessToken) { + public Member getUserInfo(String accessToken, Member member) { // 엑세스 토큰으로 카카오에서 유저 정보 받아오기 String KAUTH_USER_URL_HOST = "https://kapi.kakao.com"; @@ -70,13 +70,13 @@ public Member getUserInfo(String accessToken) { .bodyToMono(KakaoUserInfoEntity.class) .block(); - log.info("kakao AuthId : \n {} ", userInfo.getId()); - log.info("nickname : \n {} ", userInfo.getKakaoAccount().getProfile().getNickName()); - log.info("ProfileImageUrl : \n {} ", userInfo.getKakaoAccount().getProfile().getProfileImageUrl()); - log.info("kakao user info : \n {}", userInfo.toString()); + log.info("kakao AuthId : {} ", userInfo.getId()); + log.info("nickname : {} ", userInfo.getKakaoAccount().getProfile().getNickName()); + log.info("ProfileImageUrl : {} ", userInfo.getKakaoAccount().getProfile().getProfileImageUrl()); + log.info("kakao user info : {}", userInfo); // member로 변환해서 리턴. - return userInfo.toKakaoDomain(); + return userInfo.toKakaoDomain(member); } } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java index 00f8f4fb..2b477e3e 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java @@ -6,6 +6,6 @@ public interface OauthRepository { String getKakaoAccessToken(String idCode); - Member getUserInfo(String accesstoken); + Member getUserInfo(String accesstoken, Member member); } From 06093bad47bc7a0d9b267505f7d011a184b55fcd Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 20:06:33 +0900 Subject: [PATCH 19/38] =?UTF-8?q?feat=20:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85,=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/dto/request/RegisterReq.java | 34 ++++++++++++ .../depromeet/spot/domain/member/Member.java | 6 +-- .../repository/MemberRepositoryImpl.java | 2 +- .../jpa/oauth/entity/KakaoUserInfoEntity.java | 15 ++++-- .../usecase/port/in/member/MemberUsecase.java | 12 +++++ .../usecase/service/member/MemberService.java | 53 +++++++++++++++++++ 6 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 application/src/main/java/org/depromeet/spot/application/member/dto/request/RegisterReq.java create mode 100644 usecase/src/main/java/org/depromeet/spot/usecase/port/in/member/MemberUsecase.java create mode 100644 usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java diff --git a/application/src/main/java/org/depromeet/spot/application/member/dto/request/RegisterReq.java b/application/src/main/java/org/depromeet/spot/application/member/dto/request/RegisterReq.java new file mode 100644 index 00000000..0036dbb3 --- /dev/null +++ b/application/src/main/java/org/depromeet/spot/application/member/dto/request/RegisterReq.java @@ -0,0 +1,34 @@ +package org.depromeet.spot.application.member.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import org.depromeet.spot.domain.member.Member; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.Range; + +public record RegisterReq( + @NotNull(message = "인가 id code는 필수 값입니다.") + @Schema(description = "인가 id code") + String idCode, + + @NotNull(message = "닉네임 값은 필수입니다.") + @Schema(description = "설정하려는 닉네임") + @Length(min = 2, max = 10, message = "닉네임은 2글자에서 10글자 사이여야합니다.") + @Pattern(regexp = "^[a-zA-Z0-9가-힣]*$", message = "닉네임은 알파벳 대소문자, 숫자, 한글만 허용하며, 공백은 불가능합니다.") + String nickname, + + @NotNull(message = "응원 팀 선택은 필수입니다.") + @Schema(description = "응원 팀 pk") + @Range(min = 1, max = 11, message = "응원 팀은 1번(두산 베어스)부터 11번(없음)까지 입니다.") + Long teamId + ) { + + public Member toDomain(){ + return Member.builder() + .idToken(idCode) + .nickname(nickname) + .teamId(teamId) + .build(); + } +} diff --git a/domain/src/main/java/org/depromeet/spot/domain/member/Member.java b/domain/src/main/java/org/depromeet/spot/domain/member/Member.java index 9ad7425b..51144a34 100644 --- a/domain/src/main/java/org/depromeet/spot/domain/member/Member.java +++ b/domain/src/main/java/org/depromeet/spot/domain/member/Member.java @@ -10,7 +10,7 @@ @Builder public class Member { - private final Long userId; + private final Long id; private final String email; private final String name; private final String nickname; @@ -25,7 +25,7 @@ public class Member { private final LocalDateTime deletedAt; public Member( - Long userId, + Long id, String email, String name, String nickname, @@ -38,7 +38,7 @@ public Member( MemberRole role, LocalDateTime createdAt, LocalDateTime deletedAt) { - this.userId = userId; + this.id = id; this.email = email; this.name = name; this.nickname = nickname; diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java index f1d69261..97df2ac6 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java @@ -14,7 +14,7 @@ public class MemberRepositoryImpl implements MemberRepository { @Override public Member save(Member member) { - val memberEntity = memberJpaRepository.save(MemberEntity.from(member)); + MemberEntity memberEntity = memberJpaRepository.save(MemberEntity.from(member)); return memberEntity.toDomain(); } diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java index 7f6b4dbc..3a4d12d4 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java @@ -92,20 +92,27 @@ public class Profile { } } - public Member toKakaoDomain(){ + public Member toKakaoDomain(Member member){ return Member.builder() .email(kakaoAccount.email) .name(kakaoAccount.name) - .nickname(kakaoAccount.getProfile().nickName) + .nickname(member.getNickname()) .phoneNumber(kakaoAccount.phoneNumber) .profileImage(kakaoAccount.profile.profileImageUrl) .snsProvider(SnsProvider.KAKAO) - .idToken(id) - .role(MemberRole.USER) + .idToken(getId().toString()) + .role(MemberRole.ROLE_USER) + .teamId(member.getTeamId()) .createdAt(toLocalDateTime(connectedAt)) .build(); } + public Member toLoginDomain(){ + return Member.builder() + .email(kakaoAccount.email) + .build(); + } + public LocalDateTime toLocalDateTime(Date date){ return date.toInstant().atZone(ZoneId.of("Asia/Seoul")).toLocalDateTime(); } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/in/member/MemberUsecase.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/in/member/MemberUsecase.java new file mode 100644 index 00000000..89097f21 --- /dev/null +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/in/member/MemberUsecase.java @@ -0,0 +1,12 @@ +package org.depromeet.spot.usecase.port.in.member; + +import org.depromeet.spot.domain.member.Member; + +public interface MemberUsecase { + + Member create(Member member); + + Member login(String idCode); + + Boolean duplicatedNickname(String nickname); +} diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java new file mode 100644 index 00000000..b9946c88 --- /dev/null +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java @@ -0,0 +1,53 @@ +package org.depromeet.spot.usecase.service.member; + +import com.sun.jdi.request.DuplicateRequestException; +import lombok.RequiredArgsConstructor; +import org.depromeet.spot.common.exception.member.MemberException.MemberNicknameConflictException; +import org.depromeet.spot.common.exception.member.MemberException.MemberNotFoundException; +import org.depromeet.spot.domain.member.Member; +import org.depromeet.spot.usecase.port.in.member.MemberUsecase; +import org.depromeet.spot.usecase.port.out.member.MemberRepository; +import org.depromeet.spot.usecase.port.out.oauth.OauthRepository; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class MemberService implements MemberUsecase { + + private final OauthRepository oauthRepository; + + private final MemberRepository memberRepository; + + @Override + public Member create(Member member) { + if(memberRepository.existsByNickname(member.getNickname())){ + throw new MemberNicknameConflictException(); + } + String accessToken = oauthRepository.getKakaoAccessToken(member.getIdToken()); + Member memberResult = oauthRepository.getRegisterUserInfo(accessToken, member); + Member existedMember = memberRepository.findByIdToken(memberResult.getIdToken()); + if (existedMember != null) { + return existedMember; + } + + return memberRepository.save(memberResult); + } + + @Override + public Member login(String idCode) { + String accessToken = oauthRepository.getKakaoAccessToken(idCode); + Member memberResult = oauthRepository.getLoginUserInfo(accessToken); + Member existedMember = memberRepository.findByIdToken(memberResult.getIdToken()); + if(existedMember == null){ + throw new MemberNotFoundException(); + } + return existedMember; + } + + @Override + public Boolean duplicatedNickname(String nickname) { + if(memberRepository.existsByNickname(nickname)) throw new DuplicateRequestException(); + return Boolean.FALSE; + } + +} From 42cb1ba8eea6b905cde7c66ac49dac28ed0a6c4b Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 20:06:52 +0900 Subject: [PATCH 20/38] =?UTF-8?q?feat=20:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85,=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/usecase/port/out/oauth/OauthRepository.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java index 2b477e3e..534ce788 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java @@ -6,6 +6,8 @@ public interface OauthRepository { String getKakaoAccessToken(String idCode); - Member getUserInfo(String accesstoken, Member member); + Member getRegisterUserInfo(String accesstoken, Member member); + + Member getLoginUserInfo(String accesstoken); } From 69d8c81e3a86b0beb51096f2188e53ffcf7301fd Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 20:07:49 +0900 Subject: [PATCH 21/38] =?UTF-8?q?feat=20:=20id=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=EC=9D=84=20=EC=9D=B4=EC=9A=A9=ED=95=9C=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/jpa/member/repository/MemberRepositoryImpl.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java index 97df2ac6..028dc3f9 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java @@ -19,9 +19,8 @@ public Member save(Member member) { } @Override - public List findByName(String name) { - val memberEntities = memberCustomRepository.findByName(name); - return memberEntities.stream().map(MemberEntity::toDomain).toList(); + public Member findByIdToken(String idToken){ + return memberJpaRepository.findByIdToken(idToken).toDomain(); } @Override From 8027695e1627230fe0b4c14db98943d43bc80b9a Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 20:09:27 +0900 Subject: [PATCH 22/38] =?UTF-8?q?refactor=20:=20email=EA=B3=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84,=20=EC=A0=84=ED=99=94=EB=B2=88=ED=98=B8,=20=EB=A0=88?= =?UTF-8?q?=EB=B2=A8=20=EA=B6=8C=ED=95=9C=20=EB=AC=B8=EC=A0=9C=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=B4=20nullable=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/jpa/member/entity/MemberEntity.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java index 1edd14be..46b641f3 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java @@ -9,6 +9,7 @@ import org.depromeet.spot.domain.member.enums.MemberRole; import org.depromeet.spot.domain.member.enums.SnsProvider; import org.depromeet.spot.jpa.common.entity.BaseEntity; +import org.hibernate.annotations.ColumnDefault; @Entity @@ -17,19 +18,24 @@ @AllArgsConstructor public class MemberEntity extends BaseEntity { - @Column(name = "email", nullable = false, unique = true, length = 50) + // TODO : email 받아온 후 nullable = false로 바꿔야함. + @Column(name = "email", nullable = true, unique = true, length = 50) private String email; - @Column(name = "name", nullable = false, length = 20) + // TODO : 이름 받아온 후 nullable = false로 바꿔야함. + @Column(name = "name", nullable = true, length = 20) private String name; @Column(name = "nickname", nullable = false, unique = true, length = 10) private String nickname; - @Column(name = "phone_number", nullable = false, unique = true, length = 13) + // TODO : phone_number 받아온 후 nullable = false로 바꿔야함. + @Column(name = "phone_number", nullable = true, unique = true, length = 13) private String phoneNumber; - @Column(name = "level", nullable = false) + // TODO : ERD nullable로 변경 + @Column(name = "level") + @ColumnDefault("1") private Integer level; @Column(name = "profile_image", length = 255) From 61911276f76c9ecf71e5fa3c0c476d9a150103e4 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 20:09:57 +0900 Subject: [PATCH 23/38] =?UTF-8?q?refactor=20:=20url=EA=B3=BC=20clientId=20?= =?UTF-8?q?=ED=82=A4=20=EA=B0=92=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/jpa/oauth/OauthRepositoryImpl.java | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java index 1323ef21..37d95367 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java @@ -6,6 +6,7 @@ import org.depromeet.spot.jpa.oauth.entity.KakaoTokenEntity; import org.depromeet.spot.jpa.oauth.entity.KakaoUserInfoEntity; import org.depromeet.spot.usecase.port.out.oauth.OauthRepository; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatusCode; import org.springframework.stereotype.Repository; @@ -16,12 +17,19 @@ @Repository public class OauthRepositoryImpl implements OauthRepository { + // kakao에서 발급 받은 clientID + @Value("${CLIENT_ID}") + private String CLIENT_ID; + + @Value("${KAUTH_TOKEN_URL_HOST}") + private String KAUTH_TOKEN_URL_HOST; + + // 엑세스 토큰으로 카카오에서 유저 정보 받아오기 + @Value("${KAUTH_USER_URL_HOST}") + private String KAUTH_USER_URL_HOST; + @Override public String getKakaoAccessToken(String idCode) { - String KAUTH_TOKEN_URL_HOST ="https://kauth.kakao.com"; - - // kakao에서 발급 받은 clientID - String clientId = "1f043adfbb8f5438907686f472d3b164"; // Webflux의 WebClient KakaoTokenEntity kakaoTokenEntity = WebClient.create(KAUTH_TOKEN_URL_HOST).post() @@ -29,7 +37,7 @@ public String getKakaoAccessToken(String idCode) { .scheme("https") .path("/oauth/token") .queryParam("grant_type", "authorization_code") - .queryParam("client_id", clientId) + .queryParam("client_id", CLIENT_ID) .queryParam("code", idCode) .build(true)) .header(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()) @@ -43,18 +51,28 @@ public String getKakaoAccessToken(String idCode) { log.info("Access Token : {}", kakaoTokenEntity.getAccessToken()); log.info("Refresh Token : {}", kakaoTokenEntity.getRefreshToken()); -// //제공 조건: OpenID Connect가 활성화 된 앱의 토큰 발급 요청인 경우 또는 scope에 openid를 포함한 추가 항목 동의 받기 요청을 거친 토큰 발급 요청인 경우 -// log.info("Id Token : {}", kakaoTokenResponseDto.getIdToken()); -// log.info("Scope : {}", kakaoTokenResponseDto.getScope()); return kakaoTokenEntity.getAccessToken(); } @Override - public Member getUserInfo(String accessToken, Member member) { - // 엑세스 토큰으로 카카오에서 유저 정보 받아오기 - String KAUTH_USER_URL_HOST = "https://kapi.kakao.com"; + public Member getRegisterUserInfo(String accessToken, Member member) { + KakaoUserInfoEntity userInfo = getUserInfo(accessToken); + + // 회원가입 시 받은 정보를 바탕으로 member로 변환해서 리턴. + return userInfo.toKakaoDomain(member); + } + + @Override + public Member getLoginUserInfo(String accesstoken) { + KakaoUserInfoEntity userInfo = getUserInfo(accesstoken); + // TODO : idToken이 변경 될 수 있음. 등록된 email도 변경될 수 있기에 추 후 논의가 필요. + // 기존 유저와 비교를 위해선 idToken만 필요함. + return userInfo.toLoginDomain(); + } + + public KakaoUserInfoEntity getUserInfo(String accessToken){ KakaoUserInfoEntity userInfo = WebClient.create(KAUTH_USER_URL_HOST) .get() .uri(uriBuilder -> uriBuilder @@ -75,8 +93,7 @@ public Member getUserInfo(String accessToken, Member member) { log.info("ProfileImageUrl : {} ", userInfo.getKakaoAccount().getProfile().getProfileImageUrl()); log.info("kakao user info : {}", userInfo); - // member로 변환해서 리턴. - return userInfo.toKakaoDomain(member); + return userInfo; } } From 2108222ac1f7400a1d43927b8e6308be092ea958 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:53:48 +0900 Subject: [PATCH 24/38] =?UTF-8?q?feat=20:=20Authentication=EB=A5=BC=20?= =?UTF-8?q?=EC=83=81=EC=86=8D=EB=B0=9B=EC=9D=80=20jwt=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/application/common/jwt/JwtToken.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 application/src/main/java/org/depromeet/spot/application/common/jwt/JwtToken.java diff --git a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtToken.java b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtToken.java new file mode 100644 index 00000000..792d2529 --- /dev/null +++ b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtToken.java @@ -0,0 +1,52 @@ +package org.depromeet.spot.application.common.jwt; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.depromeet.spot.domain.member.enums.MemberRole; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import java.util.Collection; +import java.util.List; + +@Getter +@AllArgsConstructor +public class JwtToken implements Authentication { + // TODO : Authentication을 상속받고 UserDetail을 상속받은 커스텀 유저 정보 객체 생성해줘야함. + private String memberId; + private MemberRole memberRole; + + @Override + public Collection getAuthorities() { + return List.of(); + } + + @Override + public Object getCredentials() { + return null; + } + + @Override + public Object getDetails() { + return null; + } + + @Override + public Object getPrincipal() { + return null; + } + + @Override + public boolean isAuthenticated() { + return false; + } + + @Override + public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { + + } + + @Override + public String getName() { + return ""; + } +} From eecd86f70a75178810f0c7f97984d634023aa92d Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:02:18 +0900 Subject: [PATCH 25/38] =?UTF-8?q?refactor=20:=20=EA=B6=8C=ED=95=9C=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=EB=A1=9C=20=EC=9D=B8=ED=95=B4=20idToken?= =?UTF-8?q?=EA=B0=92=20=EB=B9=84=EA=B5=90=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java index 3a4d12d4..3f2439fc 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java @@ -109,7 +109,7 @@ public Member toKakaoDomain(Member member){ public Member toLoginDomain(){ return Member.builder() - .email(kakaoAccount.email) + .idToken(getId().toString()) .build(); } From 97e5d2204b087ea9d71d241287a6950f1b5ab480 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:07:50 +0900 Subject: [PATCH 26/38] =?UTF-8?q?docs=20:=20oauth=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=ED=99=98=EA=B2=BD=20=EB=B3=80=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- infrastructure/jpa/src/main/resources/application-jpa.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/infrastructure/jpa/src/main/resources/application-jpa.yaml b/infrastructure/jpa/src/main/resources/application-jpa.yaml index 48307996..f7a515fe 100644 --- a/infrastructure/jpa/src/main/resources/application-jpa.yaml +++ b/infrastructure/jpa/src/main/resources/application-jpa.yaml @@ -35,3 +35,7 @@ decorator: # 필요한 경우 추가 설정 +oauth: + clientId: ${CLIENT_ID} + kauthTokenUrlHost: ${KAUTH_TOKEN_URL_HOST} + kauthUserUrlHost: ${KAUTH_USER_URL_HOST} From 0ad7e309d7a6c2cad802a398f313be302402dbb2 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:11:00 +0900 Subject: [PATCH 27/38] =?UTF-8?q?fix=20:=20SecretKey=20=ED=83=80=EC=9E=85?= =?UTF-8?q?=20=EC=B0=A8=EC=9D=B4=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20=EA=B3=B5?= =?UTF-8?q?=EB=B0=B1=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/common/jwt/JwtTokenUtil.java | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtTokenUtil.java b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtTokenUtil.java index 72c75467..cc9c68bc 100644 --- a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtTokenUtil.java +++ b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtTokenUtil.java @@ -8,18 +8,17 @@ import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.UnsupportedJwtException; import io.jsonwebtoken.security.WeakKeyException; -import jakarta.annotation.PostConstruct; -import java.nio.charset.StandardCharsets; import java.security.Key; -import java.util.Base64; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.crypto.spec.SecretKeySpec; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.depromeet.spot.domain.member.MemberRole; +import org.depromeet.spot.domain.member.Member; +import org.depromeet.spot.domain.member.enums.MemberRole; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Component; @Slf4j @@ -30,12 +29,17 @@ public class JwtTokenUtil { // 토큰에 사용되는 시크릿 키 @Value("${spring.jwt.secret}") - private String secretKey; + private String SECRETKEY; - // 객체 초기화, secretKey를 Base64로 인코딩 - @PostConstruct - protected void init() { - secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes()); + public HttpHeaders getJWTToken(Member member){ + // TODO 토큰 구현하기. + + // jwt 토큰 생성 + String token = generateToken(member.getId(), member.getRole()); + + HttpHeaders headers = new HttpHeaders(); + headers.set(HttpHeaders.AUTHORIZATION, "Bearer " + token); + return headers; } public String generateToken(Long memberId, MemberRole memberRole){ @@ -44,27 +48,23 @@ public String generateToken(Long memberId, MemberRole memberRole){ .setClaims(createClaims(memberRole)) .setSubject(memberId.toString()) .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + 1000*60)) // 토큰 만료 시간 - .signWith(SignatureAlgorithm.HS256, secretKey) + .setExpiration(new Date(System.currentTimeMillis() + 1000*60*60*24*30L)) // 토큰 만료 시간 + .signWith(SignatureAlgorithm.HS256, SECRETKEY.getBytes()) .compact(); } public String getIdFromJWT(String token) { - Claims claims = Jwts.parser() - .setSigningKey(secretKey) + return Jwts.parser() + .setSigningKey(SECRETKEY.getBytes()) .parseClaimsJws(token) - .getBody(); - - return claims.get("id", String.class); + .getBody().get("id", String.class); } public String getRoleFromJWT(String token) { - Claims claims = Jwts.parser() - .setSigningKey(secretKey) + return Jwts.parser() + .setSigningKey(SECRETKEY.getBytes()) .parseClaimsJws(token) - .getBody(); - - return claims.get("role", String.class); + .getBody().get("role", String.class); } public Jws getClaims(String token){ @@ -74,7 +74,7 @@ public Jws getClaims(String token){ .parseClaimsJws(token); } - public boolean isValidToken(String token) { + public boolean isValidateToken(String token) { try { Jws claims = getClaims(token); return true; @@ -110,7 +110,7 @@ private Map createClaims(MemberRole role){ } private Key createSignature() { - byte[] apiKeySecretBytes = secretKey.getBytes(StandardCharsets.UTF_8); + byte[] apiKeySecretBytes = SECRETKEY.getBytes(); return new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS256.getJcaName()); } From c555073cc8712d87bef44e9008d5a17948c91710 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:12:55 +0900 Subject: [PATCH 28/38] =?UTF-8?q?refactor=20:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8,=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20JWT=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=B4=EC=84=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/jwt/JwtAuthenticationFilter.java | 19 +++++++---- .../member/controller/MemberController.java | 32 ++++++++++++++----- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java index e0a8bc6b..66d7ed39 100644 --- a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java +++ b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java @@ -8,8 +8,10 @@ import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.depromeet.spot.domain.member.enums.MemberRole; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.server.ResponseStatusException; @@ -26,12 +28,12 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse FilterChain filterChain) throws ServletException, IOException { List list = List.of( - // swagger, 회원가입은 제외 - "/swagger-ui/", - "/swagger-resources/", - "/api/v1/" + // 로그인, 회원가입은 제외 + "/api/v1/members", + "/kakao/" ); boolean flag = list.stream().anyMatch(url -> request.getRequestURI().startsWith(url)); + log.info("flag : {}", flag); // 현재 URL 이 LIST 안에 포함되있는걸로 시작하는가? if(flag) { filterChain.doFilter(request,response); @@ -40,13 +42,16 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse String header = request.getHeader(HttpHeaders.AUTHORIZATION); log.info("JwtAuthenticationFilter header : {}", header); + // header가 null이거나 빈 문자열이면 안됨. if(header != null && !header.equalsIgnoreCase("")){ if(header.startsWith("Bearer")){ String access_token = header.split(" ")[1]; - if(jwtTokenUtil.isValidToken(access_token)){ - String memberId = jwtTokenUtil.getIdFromJWT(header); - String role = jwtTokenUtil.getRoleFromJWT(header); + if(jwtTokenUtil.isValidateToken(access_token)){ + String memberId = jwtTokenUtil.getIdFromJWT(access_token); + MemberRole role = MemberRole.valueOf(jwtTokenUtil.getRoleFromJWT(access_token)); + JwtToken jwtToken = new JwtToken(memberId, role); + SecurityContextHolder.getContext().setAuthentication(jwtToken); filterChain.doFilter(request,response); } } diff --git a/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java b/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java index 3ad4cfdb..c4da7124 100644 --- a/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java +++ b/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java @@ -8,16 +8,15 @@ import lombok.extern.slf4j.Slf4j; import org.depromeet.spot.application.common.jwt.JwtTokenUtil; import org.depromeet.spot.application.member.dto.request.RegisterReq; -import org.depromeet.spot.application.member.dto.response.MemberResponse; import org.depromeet.spot.domain.member.Member; import org.depromeet.spot.usecase.port.in.member.MemberUsecase; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @@ -30,22 +29,39 @@ public class MemberController { private final MemberUsecase memberUsecase; + private final JwtTokenUtil jwtTokenUtil; + @PostMapping @ResponseStatus(HttpStatus.CREATED) - @Operation(summary = "Member 생성 API") - public MemberResponse create(@RequestBody MemberRequest request) { - val member = memberUsecase.create(request.name()); - return MemberResponse.from(member); + @Operation(summary = "Member 회원가입 API") + public HttpHeaders create(@RequestBody @Valid RegisterReq request) { + + Member member = request.toDomain(); + Member memberResult = memberUsecase.create(member); + + return jwtTokenUtil.getJWTToken(memberResult); + } + + @GetMapping("/{idCode}") + @ResponseStatus(HttpStatus.OK) + @Operation(summary = "Member 로그인 API") + public HttpHeaders login(@PathVariable("idCode") + @Parameter(name = "idCode", description = "sns idCode", required = true) String idCode) { + + Member member = memberUsecase.login(idCode); + + return jwtTokenUtil.getJWTToken(member); } - @GetMapping("/duplicatedNickname") + @GetMapping("/duplicatedNickname/{nickname}") @ResponseStatus(HttpStatus.OK) @Operation(summary = "닉네임 중복확인 API") public Boolean duplicatedNickname( - @RequestParam("nickname") + @PathVariable("nickname") @Parameter(name = "nickname", description = "닉네임", required = true) String nickname) { Boolean result = memberUsecase.duplicatedNickname(nickname); return result; } + } From 3232ffaa1be8e4fd8e4b12d8e231eece764769d8 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:30:49 +0900 Subject: [PATCH 29/38] =?UTF-8?q?feat=20:=20=EB=B9=8C=EB=8D=94=20=ED=8C=A8?= =?UTF-8?q?=ED=84=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/org/depromeet/spot/domain/member/Member.java | 1 + 1 file changed, 1 insertion(+) diff --git a/domain/src/main/java/org/depromeet/spot/domain/member/Member.java b/domain/src/main/java/org/depromeet/spot/domain/member/Member.java index 623bc429..ac638ee7 100644 --- a/domain/src/main/java/org/depromeet/spot/domain/member/Member.java +++ b/domain/src/main/java/org/depromeet/spot/domain/member/Member.java @@ -1,5 +1,6 @@ package org.depromeet.spot.domain.member; +import lombok.Builder; import lombok.Getter; import org.depromeet.spot.domain.member.enums.MemberRole; import org.depromeet.spot.domain.member.enums.SnsProvider; From 9e9ce8b8de42deaae540e09c147f203732a914ab Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:31:29 +0900 Subject: [PATCH 30/38] =?UTF-8?q?refactor=20:=20Member=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20get=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/application/member/dto/response/MemberResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/depromeet/spot/application/member/dto/response/MemberResponse.java b/application/src/main/java/org/depromeet/spot/application/member/dto/response/MemberResponse.java index 469e981c..06ab8203 100644 --- a/application/src/main/java/org/depromeet/spot/application/member/dto/response/MemberResponse.java +++ b/application/src/main/java/org/depromeet/spot/application/member/dto/response/MemberResponse.java @@ -5,6 +5,6 @@ public record MemberResponse(Long id, String name) { public static MemberResponse from(Member member) { - return new MemberResponse(member.getUserId(), member.getName()); + return new MemberResponse(member.getId(), member.getName()); } } From 3dd06fb96ab75748d94360cf378ed495b2835303 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:32:05 +0900 Subject: [PATCH 31/38] =?UTF-8?q?fix=20:=20spotless=20=ED=8F=AC=EB=A7=B7?= =?UTF-8?q?=ED=8C=85=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/config/SecurityConfig.java | 33 ++++--- .../common/jwt/JwtAuthenticationFilter.java | 41 ++++---- .../spot/application/common/jwt/JwtToken.java | 14 +-- .../application/common/jwt/JwtTokenUtil.java | 67 ++++++------- .../member/controller/MemberController.java | 27 +++--- .../member/dto/request/RegisterReq.java | 40 ++++---- .../exception/member/MemberException.java | 1 - .../depromeet/spot/domain/member/Member.java | 34 +++---- .../spot/jpa/member/entity/MemberEntity.java | 51 +++++----- .../repository/MemberRepositoryImpl.java | 7 +- .../spot/jpa/oauth/OauthRepositoryImpl.java | 93 ++++++++++++------- .../jpa/oauth/entity/KakaoTokenEntity.java | 12 ++- .../jpa/oauth/entity/KakaoUserInfoEntity.java | 59 ++++++------ .../port/out/oauth/OauthRepository.java | 1 - .../spot/usecase/service/MemberService.java | 1 + .../usecase/service/member/MemberService.java | 13 +-- 16 files changed, 265 insertions(+), 229 deletions(-) diff --git a/application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java b/application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java index ef4cd60d..14f73f13 100644 --- a/application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java +++ b/application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java @@ -1,6 +1,5 @@ package org.depromeet.spot.application.common.config; -import lombok.RequiredArgsConstructor; import org.depromeet.spot.application.common.jwt.JwtAuthenticationFilter; import org.depromeet.spot.application.common.jwt.JwtTokenUtil; import org.springframework.context.annotation.Bean; @@ -11,6 +10,8 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import lombok.RequiredArgsConstructor; + @Configuration @EnableWebSecurity @RequiredArgsConstructor @@ -21,20 +22,22 @@ public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http - // cross-site -> stateless라서 필요 없음. - .csrf(AbstractHttpConfigurer::disable) - // 초기 로그인 화면 필요 없음. - .formLogin(AbstractHttpConfigurer::disable) - // 토큰 방식을 사용하므로 httpBasic도 제거. - .httpBasic(AbstractHttpConfigurer::disable) - .authorizeHttpRequests( - authorize -> authorize - // 테스트, 개발 중엔 모든 경로 오픈. - .requestMatchers("/**").permitAll() - ) - // UsernamePasswordAuthenticationFilter 필터 전에 jwt 필터가 먼저 동작하도록함. - .addFilterBefore(new JwtAuthenticationFilter(jwtTokenUtil), UsernamePasswordAuthenticationFilter.class); + // cross-site -> stateless라서 필요 없음. + .csrf(AbstractHttpConfigurer::disable) + // 초기 로그인 화면 필요 없음. + .formLogin(AbstractHttpConfigurer::disable) + // 토큰 방식을 사용하므로 httpBasic도 제거. + .httpBasic(AbstractHttpConfigurer::disable) + .authorizeHttpRequests( + authorize -> + authorize + // 테스트, 개발 중엔 모든 경로 오픈. + .requestMatchers("/**") + .permitAll()) + // UsernamePasswordAuthenticationFilter 필터 전에 jwt 필터가 먼저 동작하도록함. + .addFilterBefore( + new JwtAuthenticationFilter(jwtTokenUtil), + UsernamePasswordAuthenticationFilter.class); return http.build(); } - } diff --git a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java index 66d7ed39..a5d86129 100644 --- a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java +++ b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java @@ -1,13 +1,13 @@ package org.depromeet.spot.application.common.jwt; +import java.io.IOException; +import java.util.List; + import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.List; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; + import org.depromeet.spot.domain.member.enums.MemberRole; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -16,6 +16,9 @@ import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.server.ResponseStatusException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + @RequiredArgsConstructor @Slf4j @Component @@ -24,19 +27,19 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtTokenUtil jwtTokenUtil; @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws ServletException, IOException { - - List list = List.of( - // 로그인, 회원가입은 제외 - "/api/v1/members", - "/kakao/" - ); + protected void doFilterInternal( + HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + + List list = + List.of( + // 로그인, 회원가입은 제외 + "/api/v1/members", "/kakao/"); boolean flag = list.stream().anyMatch(url -> request.getRequestURI().startsWith(url)); log.info("flag : {}", flag); // 현재 URL 이 LIST 안에 포함되있는걸로 시작하는가? - if(flag) { - filterChain.doFilter(request,response); + if (flag) { + filterChain.doFilter(request, response); return; } @@ -44,20 +47,18 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse log.info("JwtAuthenticationFilter header : {}", header); // header가 null이거나 빈 문자열이면 안됨. - if(header != null && !header.equalsIgnoreCase("")){ - if(header.startsWith("Bearer")){ + if (header != null && !header.equalsIgnoreCase("")) { + if (header.startsWith("Bearer")) { String access_token = header.split(" ")[1]; - if(jwtTokenUtil.isValidateToken(access_token)){ + if (jwtTokenUtil.isValidateToken(access_token)) { String memberId = jwtTokenUtil.getIdFromJWT(access_token); MemberRole role = MemberRole.valueOf(jwtTokenUtil.getRoleFromJWT(access_token)); JwtToken jwtToken = new JwtToken(memberId, role); SecurityContextHolder.getContext().setAuthentication(jwtToken); - filterChain.doFilter(request,response); + filterChain.doFilter(request, response); } } // 토큰 검증 실패 -> Exception } else throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); } - - } diff --git a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtToken.java b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtToken.java index 792d2529..11d7a2dc 100644 --- a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtToken.java +++ b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtToken.java @@ -1,12 +1,14 @@ package org.depromeet.spot.application.common.jwt; -import lombok.AllArgsConstructor; -import lombok.Getter; +import java.util.Collection; +import java.util.List; + import org.depromeet.spot.domain.member.enums.MemberRole; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import java.util.Collection; -import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Getter; @Getter @AllArgsConstructor @@ -41,9 +43,7 @@ public boolean isAuthenticated() { } @Override - public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { - - } + public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {} @Override public String getName() { diff --git a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtTokenUtil.java b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtTokenUtil.java index cc9c68bc..da785949 100644 --- a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtTokenUtil.java +++ b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtTokenUtil.java @@ -1,26 +1,29 @@ package org.depromeet.spot.application.common.jwt; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.Jws; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.MalformedJwtException; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.UnsupportedJwtException; -import io.jsonwebtoken.security.WeakKeyException; import java.security.Key; import java.util.Date; import java.util.HashMap; import java.util.Map; + import javax.crypto.spec.SecretKeySpec; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; + import org.depromeet.spot.domain.member.Member; import org.depromeet.spot.domain.member.enums.MemberRole; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpHeaders; import org.springframework.stereotype.Component; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.UnsupportedJwtException; +import io.jsonwebtoken.security.WeakKeyException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + @Slf4j @Component @RequiredArgsConstructor @@ -31,7 +34,7 @@ public class JwtTokenUtil { @Value("${spring.jwt.secret}") private String SECRETKEY; - public HttpHeaders getJWTToken(Member member){ + public HttpHeaders getJWTToken(Member member) { // TODO 토큰 구현하기. // jwt 토큰 생성 @@ -42,36 +45,37 @@ public HttpHeaders getJWTToken(Member member){ return headers; } - public String generateToken(Long memberId, MemberRole memberRole){ + public String generateToken(Long memberId, MemberRole memberRole) { return Jwts.builder() .setHeader(createHeader()) .setClaims(createClaims(memberRole)) .setSubject(memberId.toString()) .setIssuedAt(new Date(System.currentTimeMillis())) - .setExpiration(new Date(System.currentTimeMillis() + 1000*60*60*24*30L)) // 토큰 만료 시간 + .setExpiration( + new Date( + System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 30L)) // 토큰 만료 시간 .signWith(SignatureAlgorithm.HS256, SECRETKEY.getBytes()) .compact(); } public String getIdFromJWT(String token) { return Jwts.parser() - .setSigningKey(SECRETKEY.getBytes()) - .parseClaimsJws(token) - .getBody().get("id", String.class); + .setSigningKey(SECRETKEY.getBytes()) + .parseClaimsJws(token) + .getBody() + .get("id", String.class); } public String getRoleFromJWT(String token) { return Jwts.parser() - .setSigningKey(SECRETKEY.getBytes()) - .parseClaimsJws(token) - .getBody().get("role", String.class); + .setSigningKey(SECRETKEY.getBytes()) + .parseClaimsJws(token) + .getBody() + .get("role", String.class); } - public Jws getClaims(String token){ - return Jwts.parserBuilder() - .setSigningKey(createSignature()) - .build() - .parseClaimsJws(token); + public Jws getClaims(String token) { + return Jwts.parserBuilder().setSigningKey(createSignature()).build().parseClaimsJws(token); } public boolean isValidateToken(String token) { @@ -80,20 +84,18 @@ public boolean isValidateToken(String token) { return true; } catch (ExpiredJwtException exception) { log.error("Token Expired"); - throw new ExpiredJwtException(exception.getHeader(), exception.getClaims(),token); + throw new ExpiredJwtException(exception.getHeader(), exception.getClaims(), token); } catch (UnsupportedJwtException | WeakKeyException exception) { log.error("Unsupported Token"); throw new UnsupportedJwtException("지원되지 않는 토큰입니다."); - } catch (MalformedJwtException | IllegalArgumentException exception){ + } catch (MalformedJwtException | IllegalArgumentException exception) { throw new MalformedJwtException("잘못된 형식의 토큰입니다."); } } - - - private Map createHeader(){ + private Map createHeader() { // 헤더 생성 - Map headers = new HashMap<>(); + Map headers = new HashMap<>(); headers.put("typ", "JWT"); headers.put("alg", "HS256"); // 서명? 생성에 사용될 알고리즘 @@ -102,8 +104,8 @@ private Map createHeader(){ } // Claim -> 정보를 key-value 형태로 저장함. - private Map createClaims(MemberRole role){ - Map claims = new HashMap<>(); + private Map createClaims(MemberRole role) { + Map claims = new HashMap<>(); claims.put("role", role); return claims; @@ -113,5 +115,4 @@ private Key createSignature() { byte[] apiKeySecretBytes = SECRETKEY.getBytes(); return new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS256.getJcaName()); } - } diff --git a/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java b/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java index c4da7124..1e22cec7 100644 --- a/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java +++ b/application/src/main/java/org/depromeet/spot/application/member/controller/MemberController.java @@ -1,11 +1,7 @@ package org.depromeet.spot.application.member.controller; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; + import org.depromeet.spot.application.common.jwt.JwtTokenUtil; import org.depromeet.spot.application.member.dto.request.RegisterReq; import org.depromeet.spot.domain.member.Member; @@ -20,6 +16,12 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + @RestController @RequiredArgsConstructor @Slf4j @@ -40,13 +42,15 @@ public HttpHeaders create(@RequestBody @Valid RegisterReq request) { Member memberResult = memberUsecase.create(member); return jwtTokenUtil.getJWTToken(memberResult); - } + } @GetMapping("/{idCode}") @ResponseStatus(HttpStatus.OK) @Operation(summary = "Member 로그인 API") - public HttpHeaders login(@PathVariable("idCode") - @Parameter(name = "idCode", description = "sns idCode", required = true) String idCode) { + public HttpHeaders login( + @PathVariable("idCode") + @Parameter(name = "idCode", description = "sns idCode", required = true) + String idCode) { Member member = memberUsecase.login(idCode); @@ -57,11 +61,10 @@ public HttpHeaders login(@PathVariable("idCode") @ResponseStatus(HttpStatus.OK) @Operation(summary = "닉네임 중복확인 API") public Boolean duplicatedNickname( - @PathVariable("nickname") - @Parameter(name = "nickname", description = "닉네임", required = true) - String nickname) { + @PathVariable("nickname") + @Parameter(name = "nickname", description = "닉네임", required = true) + String nickname) { Boolean result = memberUsecase.duplicatedNickname(nickname); return result; } - } diff --git a/application/src/main/java/org/depromeet/spot/application/member/dto/request/RegisterReq.java b/application/src/main/java/org/depromeet/spot/application/member/dto/request/RegisterReq.java index 0036dbb3..bd3bf0d1 100644 --- a/application/src/main/java/org/depromeet/spot/application/member/dto/request/RegisterReq.java +++ b/application/src/main/java/org/depromeet/spot/application/member/dto/request/RegisterReq.java @@ -1,34 +1,30 @@ package org.depromeet.spot.application.member.dto.request; -import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; + import org.depromeet.spot.domain.member.Member; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Range; -public record RegisterReq( - @NotNull(message = "인가 id code는 필수 값입니다.") - @Schema(description = "인가 id code") - String idCode, - - @NotNull(message = "닉네임 값은 필수입니다.") - @Schema(description = "설정하려는 닉네임") - @Length(min = 2, max = 10, message = "닉네임은 2글자에서 10글자 사이여야합니다.") - @Pattern(regexp = "^[a-zA-Z0-9가-힣]*$", message = "닉네임은 알파벳 대소문자, 숫자, 한글만 허용하며, 공백은 불가능합니다.") - String nickname, +import io.swagger.v3.oas.annotations.media.Schema; - @NotNull(message = "응원 팀 선택은 필수입니다.") - @Schema(description = "응원 팀 pk") - @Range(min = 1, max = 11, message = "응원 팀은 1번(두산 베어스)부터 11번(없음)까지 입니다.") - Long teamId - ) { +public record RegisterReq( + @NotNull(message = "인가 id code는 필수 값입니다.") @Schema(description = "인가 id code") + String idCode, + @NotNull(message = "닉네임 값은 필수입니다.") + @Schema(description = "설정하려는 닉네임") + @Length(min = 2, max = 10, message = "닉네임은 2글자에서 10글자 사이여야합니다.") + @Pattern( + regexp = "^[a-zA-Z0-9가-힣]*$", + message = "닉네임은 알파벳 대소문자, 숫자, 한글만 허용하며, 공백은 불가능합니다.") + String nickname, + @NotNull(message = "응원 팀 선택은 필수입니다.") + @Schema(description = "응원 팀 pk") + @Range(min = 1, max = 11, message = "응원 팀은 1번(두산 베어스)부터 11번(없음)까지 입니다.") + Long teamId) { - public Member toDomain(){ - return Member.builder() - .idToken(idCode) - .nickname(nickname) - .teamId(teamId) - .build(); + public Member toDomain() { + return Member.builder().idToken(idCode).nickname(nickname).teamId(teamId).build(); } } diff --git a/common/src/main/java/org/depromeet/spot/common/exception/member/MemberException.java b/common/src/main/java/org/depromeet/spot/common/exception/member/MemberException.java index b3510004..fe5440e4 100644 --- a/common/src/main/java/org/depromeet/spot/common/exception/member/MemberException.java +++ b/common/src/main/java/org/depromeet/spot/common/exception/member/MemberException.java @@ -23,5 +23,4 @@ public MemberNicknameConflictException() { super(MemberErrorCode.MEMBER_NICKNAME_CONFLICT); } } - } diff --git a/domain/src/main/java/org/depromeet/spot/domain/member/Member.java b/domain/src/main/java/org/depromeet/spot/domain/member/Member.java index ac638ee7..d7a83b43 100644 --- a/domain/src/main/java/org/depromeet/spot/domain/member/Member.java +++ b/domain/src/main/java/org/depromeet/spot/domain/member/Member.java @@ -1,10 +1,12 @@ package org.depromeet.spot.domain.member; -import lombok.Builder; -import lombok.Getter; +import java.time.LocalDateTime; + import org.depromeet.spot.domain.member.enums.MemberRole; import org.depromeet.spot.domain.member.enums.SnsProvider; -import java.time.LocalDateTime; + +import lombok.Builder; +import lombok.Getter; @Getter @Builder @@ -25,19 +27,19 @@ public class Member { private final LocalDateTime deletedAt; public Member( - Long id, - String email, - String name, - String nickname, - String phoneNumber, - Integer level, - String profileImage, - SnsProvider snsProvider, - String idToken, - Long teamId, - MemberRole role, - LocalDateTime createdAt, - LocalDateTime deletedAt) { + Long id, + String email, + String name, + String nickname, + String phoneNumber, + Integer level, + String profileImage, + SnsProvider snsProvider, + String idToken, + Long teamId, + MemberRole role, + LocalDateTime createdAt, + LocalDateTime deletedAt) { this.id = id; this.email = email; this.name = name; diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java index 1f3d05fc..9a9c1592 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java @@ -3,14 +3,15 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; -import lombok.AllArgsConstructor; -import lombok.NoArgsConstructor; + import org.depromeet.spot.domain.member.Member; import org.depromeet.spot.domain.member.enums.MemberRole; import org.depromeet.spot.domain.member.enums.SnsProvider; import org.depromeet.spot.jpa.common.entity.BaseEntity; import org.hibernate.annotations.ColumnDefault; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; @Entity @Table(name = "members") @@ -55,32 +56,32 @@ public class MemberEntity extends BaseEntity { public static MemberEntity from(Member member) { return new MemberEntity( - member.getEmail(), - member.getName(), - member.getNickname(), - member.getPhoneNumber(), - member.getLevel(), - member.getProfileImage(), - member.getSnsProvider().getValue(), - member.getIdToken(), - member.getTeamId(), - member.getRole().getValue()); + member.getEmail(), + member.getName(), + member.getNickname(), + member.getPhoneNumber(), + member.getLevel(), + member.getProfileImage(), + member.getSnsProvider().getValue(), + member.getIdToken(), + member.getTeamId(), + member.getRole().getValue()); } public Member toDomain() { return new Member( - this.getId(), - email, - name, - nickname, - phoneNumber, - level, - profileImage, - SnsProvider.valueOf(snsProvider), - idToken, - teamId, - MemberRole.valueOf(role), - this.getCreatedAt(), - this.getDeletedAt()); + this.getId(), + email, + name, + nickname, + phoneNumber, + level, + profileImage, + SnsProvider.valueOf(snsProvider), + idToken, + teamId, + MemberRole.valueOf(role), + this.getCreatedAt(), + this.getDeletedAt()); } } diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java index 028dc3f9..5da6e05e 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java @@ -1,11 +1,12 @@ package org.depromeet.spot.jpa.member.repository; -import lombok.RequiredArgsConstructor; import org.depromeet.spot.domain.member.Member; import org.depromeet.spot.jpa.member.entity.MemberEntity; import org.depromeet.spot.usecase.port.out.member.MemberRepository; import org.springframework.stereotype.Repository; +import lombok.RequiredArgsConstructor; + @Repository @RequiredArgsConstructor public class MemberRepositoryImpl implements MemberRepository { @@ -19,7 +20,7 @@ public Member save(Member member) { } @Override - public Member findByIdToken(String idToken){ + public Member findByIdToken(String idToken) { return memberJpaRepository.findByIdToken(idToken).toDomain(); } @@ -27,6 +28,4 @@ public Member findByIdToken(String idToken){ public Boolean existsByNickname(String nickname) { return memberJpaRepository.existsByNickname(nickname); } - - } diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java index 37d95367..8b287c81 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java @@ -1,7 +1,5 @@ package org.depromeet.spot.jpa.oauth; -import io.netty.handler.codec.http.HttpHeaderValues; -import lombok.extern.slf4j.Slf4j; import org.depromeet.spot.domain.member.Member; import org.depromeet.spot.jpa.oauth.entity.KakaoTokenEntity; import org.depromeet.spot.jpa.oauth.entity.KakaoUserInfoEntity; @@ -11,6 +9,9 @@ import org.springframework.http.HttpStatusCode; import org.springframework.stereotype.Repository; import org.springframework.web.reactive.function.client.WebClient; + +import io.netty.handler.codec.http.HttpHeaderValues; +import lombok.extern.slf4j.Slf4j; import reactor.core.publisher.Mono; @Slf4j @@ -32,22 +33,33 @@ public class OauthRepositoryImpl implements OauthRepository { public String getKakaoAccessToken(String idCode) { // Webflux의 WebClient - KakaoTokenEntity kakaoTokenEntity = WebClient.create(KAUTH_TOKEN_URL_HOST).post() - .uri(uriBuilder -> uriBuilder - .scheme("https") - .path("/oauth/token") - .queryParam("grant_type", "authorization_code") - .queryParam("client_id", CLIENT_ID) - .queryParam("code", idCode) - .build(true)) - .header(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()) - .retrieve() - // TODO : Custom Exception - .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.error(new RuntimeException("Invalid Parameter"))) - .onStatus(HttpStatusCode::is5xxServerError, clientResponse -> Mono.error(new RuntimeException("Internal Server Error"))) - .bodyToMono(KakaoTokenEntity.class) - .block(); - + KakaoTokenEntity kakaoTokenEntity = + WebClient.create(KAUTH_TOKEN_URL_HOST) + .post() + .uri( + uriBuilder -> + uriBuilder + .scheme("https") + .path("/oauth/token") + .queryParam("grant_type", "authorization_code") + .queryParam("client_id", CLIENT_ID) + .queryParam("code", idCode) + .build(true)) + .header( + HttpHeaders.CONTENT_TYPE, + HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()) + .retrieve() + // TODO : Custom Exception + .onStatus( + HttpStatusCode::is4xxClientError, + clientResponse -> + Mono.error(new RuntimeException("Invalid Parameter"))) + .onStatus( + HttpStatusCode::is5xxServerError, + clientResponse -> + Mono.error(new RuntimeException("Internal Server Error"))) + .bodyToMono(KakaoTokenEntity.class) + .block(); log.info("Access Token : {}", kakaoTokenEntity.getAccessToken()); log.info("Refresh Token : {}", kakaoTokenEntity.getRefreshToken()); @@ -72,28 +84,39 @@ public Member getLoginUserInfo(String accesstoken) { return userInfo.toLoginDomain(); } - public KakaoUserInfoEntity getUserInfo(String accessToken){ - KakaoUserInfoEntity userInfo = WebClient.create(KAUTH_USER_URL_HOST) - .get() - .uri(uriBuilder -> uriBuilder - .scheme("https") - .path("/v2/user/me") - .build(true)) - .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) // access token 인가 - .header(HttpHeaders.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()) - .retrieve() - // TODO : Custom Exception - .onStatus(HttpStatusCode::is4xxClientError, clientResponse -> Mono.error(new RuntimeException("Invalid Parameter"))) - .onStatus(HttpStatusCode::is5xxServerError, clientResponse -> Mono.error(new RuntimeException("Internal Server Error"))) - .bodyToMono(KakaoUserInfoEntity.class) - .block(); + public KakaoUserInfoEntity getUserInfo(String accessToken) { + KakaoUserInfoEntity userInfo = + WebClient.create(KAUTH_USER_URL_HOST) + .get() + .uri( + uriBuilder -> + uriBuilder.scheme("https").path("/v2/user/me").build(true)) + .header( + HttpHeaders.AUTHORIZATION, + "Bearer " + accessToken) // access token 인가 + .header( + HttpHeaders.CONTENT_TYPE, + HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()) + .retrieve() + // TODO : Custom Exception + .onStatus( + HttpStatusCode::is4xxClientError, + clientResponse -> + Mono.error(new RuntimeException("Invalid Parameter"))) + .onStatus( + HttpStatusCode::is5xxServerError, + clientResponse -> + Mono.error(new RuntimeException("Internal Server Error"))) + .bodyToMono(KakaoUserInfoEntity.class) + .block(); log.info("kakao AuthId : {} ", userInfo.getId()); log.info("nickname : {} ", userInfo.getKakaoAccount().getProfile().getNickName()); - log.info("ProfileImageUrl : {} ", userInfo.getKakaoAccount().getProfile().getProfileImageUrl()); + log.info( + "ProfileImageUrl : {} ", + userInfo.getKakaoAccount().getProfile().getProfileImageUrl()); log.info("kakao user info : {}", userInfo); return userInfo; } } - diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoTokenEntity.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoTokenEntity.java index 7af922fc..ea7fd7b5 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoTokenEntity.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoTokenEntity.java @@ -1,10 +1,12 @@ package org.depromeet.spot.jpa.oauth.entity; +import org.depromeet.spot.jpa.common.entity.BaseEntity; + import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; + import lombok.Getter; import lombok.NoArgsConstructor; -import org.depromeet.spot.jpa.common.entity.BaseEntity; @Getter @NoArgsConstructor // 역직렬화를 위한 기본 생성자 @@ -13,16 +15,22 @@ public class KakaoTokenEntity extends BaseEntity { @JsonProperty("token_type") public String tokenType; + @JsonProperty("access_token") public String accessToken; + @JsonProperty("id_token") public String idToken; + @JsonProperty("expires_in") public Integer expiresIn; + @JsonProperty("refresh_token") public String refreshToken; + @JsonProperty("refresh_token_expires_in") public Integer refreshTokenExpiresIn; + @JsonProperty("scope") public String scope; -} \ No newline at end of file +} diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java index 3f2439fc..92b21188 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/entity/KakaoUserInfoEntity.java @@ -1,17 +1,20 @@ package org.depromeet.spot.jpa.oauth.entity; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; + +import org.depromeet.spot.domain.member.Member; +import org.depromeet.spot.domain.member.enums.MemberRole; +import org.depromeet.spot.domain.member.enums.SnsProvider; +import org.depromeet.spot.jpa.common.entity.BaseEntity; + import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; + import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; -import org.depromeet.spot.domain.member.Member; -import org.depromeet.spot.domain.member.enums.MemberRole; -import org.depromeet.spot.domain.member.enums.SnsProvider; -import org.depromeet.spot.jpa.common.entity.BaseEntity; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.Date; @Getter @NoArgsConstructor // 역직렬화를 위한 기본 생성자 @@ -77,44 +80,40 @@ public class KakaoAccount { @JsonIgnoreProperties(ignoreUnknown = true) public class Profile { - //닉네임 + // 닉네임 @JsonProperty("nickname") public String nickName; - //프로필 미리보기 이미지 URL + // 프로필 미리보기 이미지 URL @JsonProperty("thumbnail_image_url") public String thumbnailImageUrl; - //프로필 사진 URL + // 프로필 사진 URL @JsonProperty("profile_image_url") public String profileImageUrl; - } } - public Member toKakaoDomain(Member member){ + public Member toKakaoDomain(Member member) { return Member.builder() - .email(kakaoAccount.email) - .name(kakaoAccount.name) - .nickname(member.getNickname()) - .phoneNumber(kakaoAccount.phoneNumber) - .profileImage(kakaoAccount.profile.profileImageUrl) - .snsProvider(SnsProvider.KAKAO) - .idToken(getId().toString()) - .role(MemberRole.ROLE_USER) - .teamId(member.getTeamId()) - .createdAt(toLocalDateTime(connectedAt)) - .build(); + .email(kakaoAccount.email) + .name(kakaoAccount.name) + .nickname(member.getNickname()) + .phoneNumber(kakaoAccount.phoneNumber) + .profileImage(kakaoAccount.profile.profileImageUrl) + .snsProvider(SnsProvider.KAKAO) + .idToken(getId().toString()) + .role(MemberRole.ROLE_USER) + .teamId(member.getTeamId()) + .createdAt(toLocalDateTime(connectedAt)) + .build(); } - public Member toLoginDomain(){ - return Member.builder() - .idToken(getId().toString()) - .build(); + public Member toLoginDomain() { + return Member.builder().idToken(getId().toString()).build(); } - public LocalDateTime toLocalDateTime(Date date){ + public LocalDateTime toLocalDateTime(Date date) { return date.toInstant().atZone(ZoneId.of("Asia/Seoul")).toLocalDateTime(); } - -} \ No newline at end of file +} diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java index 534ce788..95543e08 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/oauth/OauthRepository.java @@ -9,5 +9,4 @@ public interface OauthRepository { Member getRegisterUserInfo(String accesstoken, Member member); Member getLoginUserInfo(String accesstoken); - } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java index e69de29b..8b137891 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/MemberService.java @@ -0,0 +1 @@ + diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java index b9946c88..21295a80 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java @@ -1,7 +1,5 @@ package org.depromeet.spot.usecase.service.member; -import com.sun.jdi.request.DuplicateRequestException; -import lombok.RequiredArgsConstructor; import org.depromeet.spot.common.exception.member.MemberException.MemberNicknameConflictException; import org.depromeet.spot.common.exception.member.MemberException.MemberNotFoundException; import org.depromeet.spot.domain.member.Member; @@ -10,6 +8,10 @@ import org.depromeet.spot.usecase.port.out.oauth.OauthRepository; import org.springframework.stereotype.Service; +import com.sun.jdi.request.DuplicateRequestException; + +import lombok.RequiredArgsConstructor; + @Service @RequiredArgsConstructor public class MemberService implements MemberUsecase { @@ -20,7 +22,7 @@ public class MemberService implements MemberUsecase { @Override public Member create(Member member) { - if(memberRepository.existsByNickname(member.getNickname())){ + if (memberRepository.existsByNickname(member.getNickname())) { throw new MemberNicknameConflictException(); } String accessToken = oauthRepository.getKakaoAccessToken(member.getIdToken()); @@ -38,7 +40,7 @@ public Member login(String idCode) { String accessToken = oauthRepository.getKakaoAccessToken(idCode); Member memberResult = oauthRepository.getLoginUserInfo(accessToken); Member existedMember = memberRepository.findByIdToken(memberResult.getIdToken()); - if(existedMember == null){ + if (existedMember == null) { throw new MemberNotFoundException(); } return existedMember; @@ -46,8 +48,7 @@ public Member login(String idCode) { @Override public Boolean duplicatedNickname(String nickname) { - if(memberRepository.existsByNickname(nickname)) throw new DuplicateRequestException(); + if (memberRepository.existsByNickname(nickname)) throw new DuplicateRequestException(); return Boolean.FALSE; } - } From 9e051cdbb81b9fea1d139517d565a823b3de20a1 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:51:34 +0900 Subject: [PATCH 32/38] =?UTF-8?q?fix=20:=20=EB=8B=89=EB=84=A4=EC=9E=84=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20Exception=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../depromeet/spot/usecase/service/member/MemberService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java index 21295a80..04fadbe3 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java @@ -8,8 +8,6 @@ import org.depromeet.spot.usecase.port.out.oauth.OauthRepository; import org.springframework.stereotype.Service; -import com.sun.jdi.request.DuplicateRequestException; - import lombok.RequiredArgsConstructor; @Service @@ -48,7 +46,8 @@ public Member login(String idCode) { @Override public Boolean duplicatedNickname(String nickname) { - if (memberRepository.existsByNickname(nickname)) throw new DuplicateRequestException(); + if (memberRepository.existsByNickname(nickname)) + throw new MemberNicknameConflictException(); return Boolean.FALSE; } } From 133cd0e9036b200c3377521ccd4cc0114f6e1900 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:12:55 +0900 Subject: [PATCH 33/38] =?UTF-8?q?fix=20:=20swagger=20jwtFilter=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/jwt/JwtAuthenticationFilter.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java index a5d86129..469e0508 100644 --- a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java +++ b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java @@ -33,11 +33,16 @@ protected void doFilterInternal( List list = List.of( + // swagger-ui와 v3/api-docs는 스웨거를 제외하기 위해 등록. + // 혹시나 스웨거 자원 사용 에러 발생 시 아래 두 가지 추가 필요함. + // Swagger UI에서 사용하는 외부 라ㅇ이브러리 제공 엔드포인트 : "/webjars/**" + // Swagger UI에서 사용하는 리소스 제공 엔드포인트 : "/swagger-resources/**" // 로그인, 회원가입은 제외 - "/api/v1/members", "/kakao/"); - boolean flag = list.stream().anyMatch(url -> request.getRequestURI().startsWith(url)); - log.info("flag : {}", flag); + "/swagger-ui", "/v3/api-docs", "/api/v1/members", "/kakao/"); + // 현재 URL 이 LIST 안에 포함되있는걸로 시작하는가? + boolean flag = list.stream().anyMatch(url -> request.getRequestURI().startsWith(url)); + if (flag) { filterChain.doFilter(request, response); return; From f3a46446378da97e210bd545789dabaebb0ebc0c Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:14:11 +0900 Subject: [PATCH 34/38] =?UTF-8?q?fix=20:=20=EA=B8=B0=EC=A1=B4=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=20=EC=A0=95=EB=B3=B4=EA=B0=80=20=EC=97=86=EC=9D=84=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EB=B0=9C=EC=83=9D=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jpa/member/repository/MemberJpaRepository.java | 4 +++- .../member/repository/MemberRepositoryImpl.java | 6 ++++-- .../usecase/port/out/member/MemberRepository.java | 4 +++- .../spot/usecase/service/member/MemberService.java | 14 ++++++++------ 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberJpaRepository.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberJpaRepository.java index 61d4e50e..48a2b393 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberJpaRepository.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberJpaRepository.java @@ -1,10 +1,12 @@ package org.depromeet.spot.jpa.member.repository; +import java.util.Optional; + import org.depromeet.spot.jpa.member.entity.MemberEntity; import org.springframework.data.jpa.repository.JpaRepository; public interface MemberJpaRepository extends JpaRepository { - MemberEntity findByIdToken(String idToken); + Optional findByIdToken(String idToken); Boolean existsByNickname(String nickname); } diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java index 5da6e05e..ccbeab7f 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/repository/MemberRepositoryImpl.java @@ -1,5 +1,7 @@ package org.depromeet.spot.jpa.member.repository; +import java.util.Optional; + import org.depromeet.spot.domain.member.Member; import org.depromeet.spot.jpa.member.entity.MemberEntity; import org.depromeet.spot.usecase.port.out.member.MemberRepository; @@ -20,8 +22,8 @@ public Member save(Member member) { } @Override - public Member findByIdToken(String idToken) { - return memberJpaRepository.findByIdToken(idToken).toDomain(); + public Optional findByIdToken(String idToken) { + return memberJpaRepository.findByIdToken(idToken).map(MemberEntity::toDomain); } @Override diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/member/MemberRepository.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/member/MemberRepository.java index 0258d12e..3ea7f85d 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/member/MemberRepository.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/member/MemberRepository.java @@ -1,12 +1,14 @@ package org.depromeet.spot.usecase.port.out.member; +import java.util.Optional; + import org.depromeet.spot.domain.member.Member; public interface MemberRepository { Member save(Member member); - Member findByIdToken(String idToken); + Optional findByIdToken(String idToken); Boolean existsByNickname(String nickname); } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java index 04fadbe3..88c1a085 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/member/MemberService.java @@ -1,5 +1,7 @@ package org.depromeet.spot.usecase.service.member; +import java.util.Optional; + import org.depromeet.spot.common.exception.member.MemberException.MemberNicknameConflictException; import org.depromeet.spot.common.exception.member.MemberException.MemberNotFoundException; import org.depromeet.spot.domain.member.Member; @@ -25,9 +27,9 @@ public Member create(Member member) { } String accessToken = oauthRepository.getKakaoAccessToken(member.getIdToken()); Member memberResult = oauthRepository.getRegisterUserInfo(accessToken, member); - Member existedMember = memberRepository.findByIdToken(memberResult.getIdToken()); - if (existedMember != null) { - return existedMember; + Optional existedMember = memberRepository.findByIdToken(memberResult.getIdToken()); + if (existedMember.isPresent()) { + return existedMember.get(); } return memberRepository.save(memberResult); @@ -37,11 +39,11 @@ public Member create(Member member) { public Member login(String idCode) { String accessToken = oauthRepository.getKakaoAccessToken(idCode); Member memberResult = oauthRepository.getLoginUserInfo(accessToken); - Member existedMember = memberRepository.findByIdToken(memberResult.getIdToken()); - if (existedMember == null) { + Optional existedMember = memberRepository.findByIdToken(memberResult.getIdToken()); + if (existedMember.isEmpty()) { throw new MemberNotFoundException(); } - return existedMember; + return existedMember.get(); } @Override From 7b2f5db278a7b6f6a5fc8e0de771707611f98733 Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Wed, 17 Jul 2024 23:36:16 +0900 Subject: [PATCH 35/38] =?UTF-8?q?refactor=20:=20JwtFilter=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C=20=ED=99=98=EA=B2=BD=EC=97=90=EC=84=9C=20=EB=AA=A8?= =?UTF-8?q?=EB=93=A0=20api=20=ED=97=88=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spot/application/common/jwt/JwtAuthenticationFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java index 469e0508..89c743b2 100644 --- a/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java +++ b/application/src/main/java/org/depromeet/spot/application/common/jwt/JwtAuthenticationFilter.java @@ -38,7 +38,7 @@ protected void doFilterInternal( // Swagger UI에서 사용하는 외부 라ㅇ이브러리 제공 엔드포인트 : "/webjars/**" // Swagger UI에서 사용하는 리소스 제공 엔드포인트 : "/swagger-resources/**" // 로그인, 회원가입은 제외 - "/swagger-ui", "/v3/api-docs", "/api/v1/members", "/kakao/"); + "/swagger-ui", "/v3/api-docs", "/api/v1/members", "/kakao/", "/api/v1/"); // 현재 URL 이 LIST 안에 포함되있는걸로 시작하는가? boolean flag = list.stream().anyMatch(url -> request.getRequestURI().startsWith(url)); From 44ea920da1388ef0887349f53b9e45fdf50b7cde Mon Sep 17 00:00:00 2001 From: wjdwnsdnjs13 <67488973+wjdwnsdnjs13@users.noreply.github.com> Date: Thu, 18 Jul 2024 19:53:31 +0900 Subject: [PATCH 36/38] =?UTF-8?q?fix=20:=20my=5Fteam=20nullable=EB=A1=9C?= =?UTF-8?q?=20=EC=9D=B8=ED=95=9C=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= =?UTF-8?q?(=EC=B6=94=20=ED=9B=84=20=ED=95=B4=EB=8B=B9=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=EC=99=80=20=EC=BB=AC=EB=9F=BC=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EC=98=88=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../depromeet/spot/jpa/member/entity/MemberEntity.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java index af3d4f6a..395f7e75 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/member/entity/MemberEntity.java @@ -51,6 +51,10 @@ public class MemberEntity extends BaseEntity { @Column(name = "team_id", nullable = false, length = 10) private Long teamId; + // TODO : 얘는 테이블 차이로 인한 임시 필드이므로 추후 수정해서 삭제해야함. + @Column(name = "my_team", nullable = false, length = 10) + private Long myTeam; + @Column(name = "role", nullable = false) private String role; @@ -60,11 +64,14 @@ public static MemberEntity from(Member member) { member.getName(), member.getNickname(), member.getPhoneNumber(), - member.getLevel(), + // TODO : 레벨 - 추 후 PrePersist, DynamicInsert 등을 통해 기본값 넣기. + 1, member.getProfileImage(), member.getSnsProvider().getValue(), member.getIdToken(), member.getTeamId(), + // TODO : 얘는 테이블 차이로 인한 임시 필드이므로 추후 수정해서 삭제해야함. + member.getTeamId(), member.getRole().getValue()); } From 53ca3989d3fef2246d7bb0fb5ddf3a52a07874e5 Mon Sep 17 00:00:00 2001 From: EunjiShin Date: Thu, 18 Jul 2024 20:19:41 +0900 Subject: [PATCH 37/38] =?UTF-8?q?feat:=20jwt=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/src/main/resources/application-jwt.yml | 2 ++ application/src/main/resources/application.yaml | 5 ++--- .../spot/jpa/oauth/OauthRepositoryImpl.java | 14 +++----------- 3 files changed, 7 insertions(+), 14 deletions(-) create mode 100644 application/src/main/resources/application-jwt.yml diff --git a/application/src/main/resources/application-jwt.yml b/application/src/main/resources/application-jwt.yml new file mode 100644 index 00000000..15dc8a24 --- /dev/null +++ b/application/src/main/resources/application-jwt.yml @@ -0,0 +1,2 @@ +jwt: + secret: ${JWT_SECRETKEY} \ No newline at end of file diff --git a/application/src/main/resources/application.yaml b/application/src/main/resources/application.yaml index 08f50347..df36d043 100644 --- a/application/src/main/resources/application.yaml +++ b/application/src/main/resources/application.yaml @@ -8,9 +8,8 @@ spring: include: - jpa - ncp + - jwt # swagger를 이용해 API 명세서 생성 doc: swagger-ui: - path: /swagger-ui.html - jwt: - secret: ${JWT_SECRETKEY} \ No newline at end of file + path: /swagger-ui.html \ No newline at end of file diff --git a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java index 8b287c81..41f59175 100644 --- a/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java +++ b/infrastructure/jpa/src/main/java/org/depromeet/spot/jpa/oauth/OauthRepositoryImpl.java @@ -31,7 +31,6 @@ public class OauthRepositoryImpl implements OauthRepository { @Override public String getKakaoAccessToken(String idCode) { - // Webflux의 WebClient KakaoTokenEntity kakaoTokenEntity = WebClient.create(KAUTH_TOKEN_URL_HOST) @@ -61,9 +60,9 @@ public String getKakaoAccessToken(String idCode) { .bodyToMono(KakaoTokenEntity.class) .block(); - log.info("Access Token : {}", kakaoTokenEntity.getAccessToken()); - log.info("Refresh Token : {}", kakaoTokenEntity.getRefreshToken()); - + if (kakaoTokenEntity == null) { + // TODO + } return kakaoTokenEntity.getAccessToken(); } @@ -110,13 +109,6 @@ public KakaoUserInfoEntity getUserInfo(String accessToken) { .bodyToMono(KakaoUserInfoEntity.class) .block(); - log.info("kakao AuthId : {} ", userInfo.getId()); - log.info("nickname : {} ", userInfo.getKakaoAccount().getProfile().getNickName()); - log.info( - "ProfileImageUrl : {} ", - userInfo.getKakaoAccount().getProfile().getProfileImageUrl()); - log.info("kakao user info : {}", userInfo); - return userInfo; } } From 4de5d45aeb9e4e4c07c62f6ca9053e1c600dd398 Mon Sep 17 00:00:00 2001 From: EunjiShin Date: Thu, 18 Jul 2024 20:22:18 +0900 Subject: [PATCH 38/38] feat: gitignore update --- .gitignore | 4 +++- application/.gitignore | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 754acf39..03e49cd7 100644 --- a/.gitignore +++ b/.gitignore @@ -382,4 +382,6 @@ gradle-app.setting # End of https://www.toptal.com/developers/gitignore/api/macos,windows,intellij,intellij+iml,intellij+all,visualstudiocode,java,gradle,kotlin /db/ -.env \ No newline at end of file +.env + +*.application-jwt.yml \ No newline at end of file diff --git a/application/.gitignore b/application/.gitignore index b63da455..59c70a62 100644 --- a/application/.gitignore +++ b/application/.gitignore @@ -39,4 +39,6 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store + +*.application-jwt.yml \ No newline at end of file