Skip to content

Commit

Permalink
Merge branch 'main' into feat/BSVR-232
Browse files Browse the repository at this point in the history
# Conflicts:
#	.gitignore
#	application/build.gradle.kts
#	application/src/main/java/org/depromeet/spot/application/common/config/SecurityConfig.java
#	application/src/main/resources/application.yaml
  • Loading branch information
wjdwnsdnjs13 committed Aug 29, 2024
2 parents d4bbaf3 + 5c1b358 commit 5f8f3b1
Show file tree
Hide file tree
Showing 43 changed files with 1,077 additions and 53 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/dev-build-and-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,20 @@ jobs:
-e OAUTH_CLIENTID=${{ secrets.KAKAO_CLIENT_ID }} \
-e OAUTH_KAUTHTOKENURLHOST=${{ secrets.KAUTH_TOKEN_URL_HOST }} \
-e OAUTH_KAUTHUSERURLHOST=${{ secrets.KAUTH_USER_URL_HOST }} \
-e OAUTH_KAKAOCLIENTID=${{ secrets.OAUTH_KAKAOCLIENTID }} \
-e OAUTH_KAKAOAUTHTOKENURLHOST=${{ secrets.KAKAOAUTHTOKENURLHOST }} \
-e OAUTH_KAKAOAUTHUSERURLHOST=${{ secrets.KAKAOAUTHUSERURLHOST }} \
-e OAUTH_KAKAOREDIRECTURL=${{ secrets.KAKAOREDIRECTURL }} \
-e OAUTH_GOOGLECLIENTID=${{ secrets.GOOGLECLIENTID }} \
-e OAUTH_GOOGLECLIENTSECRET=${{ secrets.GOOGLECLIENTSECRET }} \
-e OAUTH_GOOGLEREDIRECTURL=${{ secrets.GOOGLEREDIRECTURL }} \
-e OAUTH_GOOGLEAUTHTOKENURLHOST=${{ secrets.GOOGLEAUTHTOKENURLHOST }} \
-e OAUTH_GOOGLEUSERURLHOST=${{ secrets.GOOGLEUSERURLHOST }} \
-e SPRING_JPA_HIBERNATE_DDL_AUTO=validate \
-e AWS_S3_ACCESS_KEY=${{ secrets.AWS_S3_ACCESS_KEY }} \
-e AWS_S3_SECRET_KEY=${{ secrets.AWS_S3_SECRET_KEY }} \
-e AWS_S3_BUCKET_NAME=${{ secrets.DEV_AWS_S3_BUCKET_NAME }} \
-e AWS_S3_BASICPROFILEIMAGEURL=${{ secrets.BASICPROFILEIMAGEURL }} \
-e TZ=Asia/Seoul \
-e SENTRY_DSN=${{ secrets.SENTRY_DSN }} \
-e SENTRY_ENABLE_TRACING=true \
Expand Down
12 changes: 11 additions & 1 deletion .github/workflows/manual-prod-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,23 @@ jobs:
-e SPRING_DATASOURCE_USERNAME=${{ secrets.PROD_DB_USERNAME }} \
-e SPRING_DATASOURCE_PASSWORD=${{ secrets.PROD_DB_PASSWORD }} \
-e SPRING_JWT_SECRET=${{ secrets.JWT_SECRET }} \
-e OAUTH_CLIENTID=${{ secrets.KAKAO_CLIENT_ID }} \
-e KAKAO_CLIENT_ID=${{ secrets.KAKAO_CLIENT_ID }} \
-e OAUTH_KAUTHTOKENURLHOST=${{ secrets.KAUTH_TOKEN_URL_HOST }} \
-e OAUTH_KAUTHUSERURLHOST=${{ secrets.KAUTH_USER_URL_HOST }} \
-e OAUTH_KAKAOCLIENTID=${{ secrets.OAUTH_KAKAOCLIENTID }} \
-e OAUTH_KAKAOAUTHTOKENURLHOST=${{ secrets.KAKAOAUTHTOKENURLHOST }} \
-e OAUTH_KAKAOAUTHUSERURLHOST=${{ secrets.KAKAOAUTHUSERURLHOST }} \
-e OAUTH_KAKAOREDIRECTURL=${{ secrets.KAKAOREDIRECTURL }} \
-e OAUTH_GOOGLECLIENTID=${{ secrets.GOOGLECLIENTID }} \
-e OAUTH_GOOGLECLIENTSECRET=${{ secrets.GOOGLECLIENTSECRET }} \
-e OAUTH_GOOGLEREDIRECTURL=${{ secrets.GOOGLEREDIRECTURL }} \
-e OAUTH_GOOGLEAUTHTOKENURLHOST=${{ secrets.GOOGLEAUTHTOKENURLHOST }} \
-e OAUTH_GOOGLEUSERURLHOST=${{ secrets.GOOGLEUSERURLHOST }} \
-e SPRING_JPA_HIBERNATE_DDL_AUTO=validate \
-e AWS_S3_ACCESS_KEY=${{ secrets.AWS_S3_ACCESS_KEY }} \
-e AWS_S3_SECRET_KEY=${{ secrets.AWS_S3_SECRET_KEY }} \
-e AWS_S3_BUCKET_NAME=${{ secrets.PROD_AWS_S3_BUCKET_NAME }} \
-e AWS_S3_BASICPROFILEIMAGEURL=${{ secrets.BASICPROFILEIMAGEURL }} \
-e TZ=Asia/Seoul \
-e SENTRY_DSN=${{ secrets.SENTRY_DSN }} \
-e SENTRY_ENABLE_TRACING=true \
Expand Down
7 changes: 5 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,10 @@ gradle-app.setting
*.application-jwt.yml
*.application-monitoring.yml
application-jwt.yml
application-kakao.yml
application-oauth.yml
application-sentry.yml
application-aws.yaml
application-mixpanel.yaml
application-mixpanel.yaml

# 민성 레디스 바이너리 파일
redis-server-7.2.3-mac-arm64
6 changes: 6 additions & 0 deletions application/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ dependencies {
// Mixpanel
implementation("com.mixpanel:mixpanel-java:_")

// test container
testImplementation("org.testcontainers:testcontainers:_")
testImplementation("org.testcontainers:junit-jupiter:_")
testImplementation("org.testcontainers:mysql:_")
testImplementation("org.testcontainers:jdbc:_")

}

// spring boot main application이므로 실행 가능한 jar를 생성한다.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class SecurityConfig {
"/favicon.ico/**",
"/api/v1/members/**",
"/actuator/**",
"/login/oauth2/code/google/**",
"/trackEvent"
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

import org.depromeet.spot.infrastructure.InfrastructureConfig;
import org.depromeet.spot.usecase.config.UsecaseConfig;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@ComponentScan(basePackages = {"org.depromeet.spot.application"})
@Configuration
@EnableConfigurationProperties
@ComponentScan(basePackages = {"org.depromeet.spot.application"})
@ConfigurationPropertiesScan(basePackages = {"org.depromeet.spot.application"})
@Import(value = {UsecaseConfig.class, SwaggerConfig.class, InfrastructureConfig.class})
public class SpotApplicationConfig {}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
Map.of(
"/api/v1/members",
Set.of("GET", "POST"),
"/api/v2/members",
Set.of("GET", "POST"),
"/login/oauth2/code/google",
Set.of("GET"),
"/api/v1/members/delete",
Set.of("DELETE"),
"/api/v1/baseball-teams",
Expand All @@ -66,7 +70,6 @@ protected void doFilterInternal(
filterChain.doFilter(request, response);
return;
}

// header가 null이거나 빈 문자열이면 안됨.
if (header == null || header.isEmpty()) {
throw new CustomJwtException(JwtErrorCode.NONEXISTENT_TOKEN);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.depromeet.spot.application.common.jwt;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "spring.jwt")
public record JwtProperties(String secret) {}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import org.depromeet.spot.application.common.exception.JwtErrorCode;
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.stereotype.Component;

import io.jsonwebtoken.Claims;
Expand All @@ -37,8 +36,7 @@ public class JwtTokenUtil {
// JWT를 생성하고 관리하는 클래스

// 토큰에 사용되는 시크릿 키
@Value("${spring.jwt.secret}")
private String SECRETKEY;
private final JwtProperties properties;

public String getJWTToken(Member member) {
// TODO 토큰 구현하기.
Expand All @@ -61,21 +59,21 @@ public String generateToken(Long memberId, MemberRole memberRole) {
.setClaims(createClaims(memberId, memberRole))
.setIssuedAt(current)
.setExpiration(expiredAt)
.signWith(SignatureAlgorithm.HS256, SECRETKEY.getBytes())
.signWith(SignatureAlgorithm.HS256, properties.secret().getBytes())
.compact();
}

public Long getIdFromJWT(String token) {
return Jwts.parser()
.setSigningKey(SECRETKEY.getBytes())
.setSigningKey(properties.secret().getBytes())
.parseClaimsJws(token)
.getBody()
.get("memberId", Long.class);
}

public String getRoleFromJWT(String token) {
return Jwts.parser()
.setSigningKey(SECRETKEY.getBytes())
.setSigningKey(properties.secret().getBytes())
.parseClaimsJws(token)
.getBody()
.get("role", String.class);
Expand Down Expand Up @@ -124,7 +122,7 @@ private Map<String, Object> createClaims(Long memberId, MemberRole role) {
}

private Key createSignature() {
byte[] apiKeySecretBytes = SECRETKEY.getBytes();
byte[] apiKeySecretBytes = properties.secret().getBytes();
return new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS256.getJcaName());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package org.depromeet.spot.application.member.controller;

import jakarta.validation.Valid;

import org.depromeet.spot.application.common.jwt.JwtTokenUtil;
import org.depromeet.spot.application.member.dto.request.RegisterV2Req;
import org.depromeet.spot.application.member.dto.response.JwtTokenResponse;
import org.depromeet.spot.domain.member.Member;
import org.depromeet.spot.domain.member.enums.SnsProvider;
import org.depromeet.spot.usecase.port.in.oauth.OauthUsecase;
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.RequestParam;
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
@Tag(name = "(v2) Oauth")
public class OauthController {

private final OauthUsecase oauthUsecase;

private final JwtTokenUtil jwtTokenUtil;

@PostMapping("/api/v2/members")
@ResponseStatus(HttpStatus.CREATED)
@Operation(summary = "Member 회원가입 API")
public JwtTokenResponse create(@RequestBody @Valid RegisterV2Req request) {

Member member = request.toDomain();
Member memberResult = oauthUsecase.create(request.accessToken(), member);

return new JwtTokenResponse(jwtTokenUtil.getJWTToken(memberResult));
}

@GetMapping("/api/v2/members/{snsProvider}/{token}")
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Member 로그인 API")
public JwtTokenResponse login(
@PathVariable("snsProvider")
@Parameter(name = "snsProvider", description = "KAKAO/GOOGLE", required = true)
SnsProvider snsProvider,
@PathVariable("token")
@Parameter(
name = "token",
description = "sns 카카오는 accessToken, 구글은 authToken",
required = true)
String token) {

Member member = oauthUsecase.login(snsProvider, token);
return new JwtTokenResponse(jwtTokenUtil.getJWTToken(member));
}

// TODO : /api/v2/members를 RequestMapping으로 빼면 구글 로그인에서 4xx Exception 발생
@GetMapping("/api/v2/members/authorization/{snsProvider}")
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "(백엔드용)accessToken을 받아오기 위한 API")
public String getAccessToken2(
@PathVariable("snsProvider") SnsProvider snsProvider, @RequestParam String code) {
String token = oauthUsecase.getOauthAccessToken(snsProvider, code);
log.info("snsProvider : {}", snsProvider);
log.info("token : \n{}", token);
return token;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.depromeet.spot.application.member.dto.request;

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;

import org.depromeet.spot.domain.member.Member;
import org.depromeet.spot.domain.member.enums.SnsProvider;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.Range;

import io.swagger.v3.oas.annotations.media.Schema;

public record RegisterV2Req(
@NotNull(message = "인가 accessToken는 필수 값입니다.") @Schema(description = "카카오 인증 accessToken")
String accessToken,
@NotNull(message = "닉네임 값은 필수입니다.")
@Schema(description = "설정하려는 닉네임")
@Length(min = 2, max = 10, message = "닉네임은 2글자에서 10글자 사이여야합니다.")
@Pattern(
regexp = "^[a-zA-Z0-9가-힣]*$",
message = "닉네임은 알파벳 대소문자, 숫자, 한글만 허용하며, 공백은 불가능합니다.")
String nickname,
@Schema(description = "응원 팀 pk")
@Range(
min = 1,
max = 10,
message = "응원 팀은 null(모두 응원), 1번(두산 베어스)부터 10번(NC 다이노스)까지 입니다.")
Long teamId,
@NotNull(message = "SNS Provider는 필수 값입니다.") @Schema(description = "KAKAO/GOOGLE")
SnsProvider snsProvider) {

public Member toDomain() {
return Member.builder().nickname(nickname).teamId(teamId).snsProvider(snsProvider).build();
}
}
6 changes: 3 additions & 3 deletions application/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ spring:
- jpa
- aws
- jwt
- kakao
- oauth
- mixpanel
dev:
- jpa
- aws
- jwt
- kakao
- oauth
- monitoring
- mixpanel
prod:
- jpa
- aws
- jwt
- kakao
- oauth
- sentry
- monitoring
- mixpanel
Expand Down
Loading

0 comments on commit 5f8f3b1

Please sign in to comment.