Skip to content

Commit

Permalink
Merge pull request #313 from JNU-econovation/be
Browse files Browse the repository at this point in the history
[BE] merge to main
  • Loading branch information
kssumin authored Mar 12, 2024
2 parents 7e2fe60 + 5f90bb2 commit 4d58bdd
Show file tree
Hide file tree
Showing 58 changed files with 604 additions and 511 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/backend-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:

- name: DB를 실행
run: |
docker-compose -f resources/test-develop-environment/docker-compose.yml up -d
docker-compose -f resources/local-develop-environment/docker-compose.yml up -d
sleep 20
- name: Gradle 캐싱
Expand Down
Empty file removed BE/BE.md
Empty file.
25 changes: 9 additions & 16 deletions BE/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,16 @@
| 운영 환경 구축 | EC2, Docker |
| Build | Gradle |
| CI/CD | Github Actions |
| Library | Spring Open Feign, OAuth 2.0, Actuator |
| Library | Spring Open Feign, OAuth 2.0, spring actuator |

## 실행방법
1. git clone
1. git clone [repository url)
2. 환경변수를 설정해줍니다.(env.properties)
3. docker-compose를 통해 db를 실행합니다.
4. spring boot를 실행합니다.


## docs
[서브 도메인 적용하기](https://velog.io/@kssumin/%EC%84%9C%EB%B8%8C-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0)

[타임존 설정](https://velog.io/@kssumin/DB%EC%97%90-%EB%93%A4%EC%96%B4%EA%B0%80%EB%8A%94-%EC%8B%9C%EA%B0%84%EC%9D%B4-%EC%9D%B4%EC%83%81%ED%95%98%EB%8B%A4)

[분기 처리 코드 리팩터링하기](https://velog.io/@kssumin/%EB%B6%84%EA%B8%B0%EC%B2%98%EB%A6%AC-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-%ED%95%98%EA%B8%B0)

[슬랙 API 호출 코드를 추상화하기](https://velog.io/@kssumin/%EC%8A%AC%EB%9E%99-API-%ED%98%B8%EC%B6%9C-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%B6%94%EC%83%81%ED%99%94%ED%95%98%EA%B8%B0)

[200 status code만 사용하지 말자](https://velog.io/@kssumin/EEOS-%EC%BA%90%EC%8B%B1)
3. db를 실행합니다.
```shell
cd resources
cd local-develop-environment
docker-compose up
```
5. spring boot를 실행합니다.

4 changes: 4 additions & 0 deletions BE/eeos/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ dependencies {
implementation 'org.flywaydb:flyway-core'
implementation 'org.flywaydb:flyway-mysql'

// redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'


// openfeign
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

Expand Down
14 changes: 12 additions & 2 deletions BE/eeos/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
environment:
- MYSQL_DATABASE=eeos
- MYSQL_ROOT_HOST=%
- MYSQL_ROOT_PASSWORD=root
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
command: [ "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci", "--skip-character-set-client-handshake", "--lower_case_table_names=1", "--max_connections=2048", "--wait_timeout=3600" ]
ports:
- "13306:3306"
Expand All @@ -25,6 +25,15 @@ services:
- eeos-network
depends_on:
- mysql
redis:
container_name: eeos-redis
image: redis:latest
ports:
- "16379:6379"
environment:
- REDIS_PASSWORD=${DB_PASSWORD}
networks:
- eeos-network
backend:
build:
context: .
Expand All @@ -35,12 +44,13 @@ services:
depends_on:
- mysql
- flyway
- redis
restart: always
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://eeos-mysql:3306/eeos?useSSL=false&serverTimezone=Asia/Seoul&useLegacyDatetimeCode=false&allowPublicKeyRetrieval=true
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: root
SPRING_PROFILES_ACTIVE: dev
SPRING_PROFILES_ACTIVE: ${PROFILE}
networks: #사용할 네트워크 지정
- eeos-network
networks:
Expand Down
14 changes: 10 additions & 4 deletions BE/eeos/resources/local-develop-environment/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
version: '3.1'
services:
overflow-mysql:
eeos-local-mysql:
container_name: eeos-mysql8
image: mysql/mysql-server:8.0.27
environment:
Expand All @@ -12,13 +12,19 @@ services:
- "13308:3306"
volumes:
- ./mysql-init.d/:/docker-entrypoint-initdb.d

overflow-adminer: # mysql web admin
eeos-local-redis:
container_name: eeos-redis
image: redis:latest
ports:
- "16379:6379"
environment:
- REDIS_PASSWORD=root
eeos-local-adminer: # mysql web admin
container_name: eeos-adminer
image: adminer:4
ports:
- "18080:8080"
environment:
- ADMINER_DEFAULT_SERVER=overflow-mysql8
- ADMINER_DESIGN=nette
- ADMINER_PLUGINS=tables-filter tinymce
- ADMINER_PLUGINS=tables-filter tinymce
14 changes: 0 additions & 14 deletions BE/eeos/resources/test-develop-environment/docker-compose.yml

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.blackcompany.eeos.auth.application.domain.converter;

import com.blackcompany.eeos.auth.application.domain.OauthMemberModel;
import com.blackcompany.eeos.auth.persistence.OAuthMemberEntity;
import com.blackcompany.eeos.common.support.converter.AbstractEntityConverter;
import org.springframework.stereotype.Component;

@Component
public class OauthMemberEntityConverter
implements AbstractEntityConverter<OAuthMemberEntity, OauthMemberModel> {
@Override
public OauthMemberModel from(final OAuthMemberEntity entity) {
return OauthMemberModel.builder().oauthId(entity.getOauthId()).build();
}

@Override
public OAuthMemberEntity toEntity(final OauthMemberModel model) {
return OAuthMemberEntity.builder().oauthId(model.getOauthId()).build();
}

public OAuthMemberEntity toEntity(final String oauthId, Long memberId) {
return OAuthMemberEntity.builder().oauthId(oauthId).memberId(memberId).build();
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.blackcompany.eeos.auth.application.domain.token;

import com.blackcompany.eeos.auth.application.exception.NotFoundCookieException;
import com.blackcompany.eeos.auth.application.exception.NotFoundHeaderTokenException;
import com.blackcompany.eeos.auth.application.exception.RtTokenExpiredException;
import com.blackcompany.eeos.auth.application.exception.TokenExpiredException;
import com.blackcompany.eeos.auth.application.exception.TokenParsingException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
Expand All @@ -27,61 +25,57 @@ public class TokenResolver {
public TokenResolver(
@Value("${security.jwt.access.secretKey}") String accessSecretKey,
@Value("${security.jwt.refresh.secretKey}") String refreshSecretKey) {
this.accessSecretKey = Keys.hmacShaKeyFor(accessSecretKey.getBytes(StandardCharsets.UTF_8));
this.refreshSecretKey = Keys.hmacShaKeyFor(refreshSecretKey.getBytes(StandardCharsets.UTF_8));
this.accessSecretKey = generateSecretKey(accessSecretKey);
this.refreshSecretKey = generateSecretKey(refreshSecretKey);
}

public Long getExpiredDateByHeader(final String token) {
Date expiration = getAccessClaims(token).getExpiration();

return expiration.getTime();
public Long getExpiredDateByAccessToken(final String token) {
return getExpirationTime(getAccessClaims(token));
}

public Long getUserInfoByCookie(final String token) {
return getRefreshClaims(token).get(MEMBER_ID_CLAIM_KEY, Long.class);
public Long getExpiredDateByRefreshToken(final String token) {
return getExpirationTime(getRefreshClaims(token));
}

public Long getExpiredDateByCookie(final String token) {
Date expiration = getRefreshClaims(token).getExpiration();
public Long getUserDataByAccessToken(final String token) {
return getClaimValue(getAccessClaims(token), MEMBER_ID_CLAIM_KEY);
}

return expiration.getTime();
public Long getUserDataByRefreshToken(final String token) {
return getClaimValue(getRefreshClaims(token), MEMBER_ID_CLAIM_KEY);
}

public Long getUserInfoByHeader(final String token) {
return getAccessClaims(token).get(MEMBER_ID_CLAIM_KEY, Long.class);
private SecretKey generateSecretKey(String key) {
return Keys.hmacShaKeyFor(key.getBytes(StandardCharsets.UTF_8));
}

private Claims getAccessClaims(final String token) {
try {
return Jwts.parserBuilder()
.setSigningKey(accessSecretKey)
.build()
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException e) {
throw new TokenExpiredException();
} catch (SignatureException e) {
log.error("jwt 파싱에 에러가 발생했습니다.");
throw new NotFoundHeaderTokenException();
} catch (IllegalStateException e) {
throw new NotFoundHeaderTokenException();
}
return parseClaims(token, accessSecretKey);
}

private Claims getRefreshClaims(final String token) {
return parseClaims(token, refreshSecretKey);
}

private Claims parseClaims(final String token, final SecretKey secretKey) {
try {
return Jwts.parserBuilder()
.setSigningKey(refreshSecretKey)
.build()
.parseClaimsJws(token)
.getBody();
return Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token).getBody();
} catch (ExpiredJwtException e) {
throw new RtTokenExpiredException();
throw new TokenExpiredException();
} catch (SignatureException e) {
log.error("jwt 파싱에 에러가 발생했습니다.");
throw new NotFoundCookieException();
} catch (IllegalStateException e) {
throw new NotFoundCookieException();
throw new TokenParsingException(e);
} catch (Exception e) {
log.error("JWT 파싱 중 오류 발생: {}", e.getMessage(), e);
throw new TokenParsingException(e);
}
}

private Long getExpirationTime(Claims claims) {
Date expiration = claims.getExpiration();
return expiration.getTime();
}

private Long getClaimValue(Claims claims, String claimKey) {
return claims.get(claimKey, Long.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.blackcompany.eeos.auth.application.event;

import lombok.Getter;

@Getter
public class DeletedMemberEvent {
private final Long memberId;
private final String token;

private DeletedMemberEvent(Long memberId, String token) {
this.memberId = memberId;
this.token = token;
}

/**
* 멤버 탈퇴 이벤트를 발행한다.
*
* @param memberId 회원 탈퇴를 원하는 멤버 id
* @param token 회원 탈퇴 시 사용한 토큰
* @return
*/
public static DeletedMemberEvent of(Long memberId, String token) {
return new DeletedMemberEvent(memberId, token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.blackcompany.eeos.auth.application.event;

import com.blackcompany.eeos.auth.application.domain.token.TokenResolver;
import com.blackcompany.eeos.auth.persistence.BlackAuthenticationRepository;
import com.blackcompany.eeos.target.persistence.AttendRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

@Component
@RequiredArgsConstructor
@Slf4j
public class DeletedMemberEventListener {
private final AttendRepository attendRepository;
private final BlackAuthenticationRepository blackAuthenticationRepository;
private final TokenResolver tokenResolver;

@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handleDeletedProgram(DeletedMemberEvent event) {
deleteTargetData(event.getMemberId());
saveUsedToken(event.getToken(), event.getMemberId());
}

private void deleteTargetData(Long memberId) {
attendRepository.deleteAllByMemberId(memberId);
}

private void saveUsedToken(String token, Long memberId) {
blackAuthenticationRepository.save(token, memberId, getExpiredDate(token));
}

private Long getExpiredDate(String token) {
return tokenResolver.getExpiredDateByRefreshToken(token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.blackcompany.eeos.auth.application.exception;

import com.blackcompany.eeos.common.exception.BusinessException;
import org.springframework.http.HttpStatus;

/** JWT 파싱 중 발생한 예외 */
public class TokenParsingException extends BusinessException {
private static final String FAIL_CODE = "4007";
private String message;

public TokenParsingException(Exception e) {
super(FAIL_CODE, HttpStatus.BAD_REQUEST);
message = e.getMessage();
}

@Override
public String getMessage() {
return String.format("JWT 파싱 중 오류 발생: {}", message);
}
}
Loading

0 comments on commit 4d58bdd

Please sign in to comment.