Skip to content

Commit

Permalink
Merge branch 'BE/dev' into BE/feature/#681-db-refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
koust6u authored Oct 14, 2024
2 parents 40b1956 + 7e3d30d commit f88aba2
Show file tree
Hide file tree
Showing 94 changed files with 1,790 additions and 468 deletions.
12 changes: 8 additions & 4 deletions .github/workflows/be_cd-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,20 @@ jobs:
# Docker Hub info from Github Secrets
DOCKER_REPO_NAME=${{ secrets.DOCKER_REPO_NAME }}
# DB Configuration secrets info from Github Secrets
# DB Configuration secrets info from Github Secrets
MYSQL_DB_NAME=${{ secrets.MYSQL_DB_NAME }}
MYSQL_TIME_ZONE=${{ secrets.MYSQL_TIME_ZONE }}
DB_BINDING_PORT=${{ secrets.DB_BINDING_PORT }}
DOCKER_DATA_PATH=${{ secrets.DOCKER_DATA_PATH }}
DB_URL=${{ secrets.DB_URL }}
DB_USERNAME=${{ secrets.DB_USERNAME }}
DB_PASSWORD=${{ secrets.DB_PASSWORD }}
MASTER_DB_URL=${{ secrets.MASTER_DB_URL }}
MASTER_DB_USERNAME=${{ secrets.MASTER_DB_USERNAME }}
MASTER_DB_PASSWORD=${{ secrets.MASTER_DB_PASSWORD }}
SLAVE_DB_URL=${{ secrets.SLAVE_DB_URL }}
SLAVE_DB_USERNAME=${{ secrets.SLAVE_DB_USERNAME }}
SLAVE_DB_PASSWORD=${{ secrets.SLAVE_DB_PASSWORD }}
DDL_AUTO=${{ secrets.DDL_AUTO }}
# OAUTH & JWT
CLIENT_ID=${{ secrets.CLIENT_ID }}
CLIENT_SECRET=${{ secrets.CLIENT_SECRET }}
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/be_cd-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ jobs:
DOCKER_REPO_NAME=${{ secrets.DOCKER_REPO_NAME }}
# DB Configuration secrets info from Github Secrets
MASTER_DB_URL=${{ secrets.MASTER_DB_URL }}
MASTER_DB_USERNAME=${{ secrets.MASTER_DB_USERNAME }}
MASTER_DB_PASSWORD=${{ secrets.MASTER_DB_PASSWORD }}
MASTER_DB_URL=${{ secrets.TEST_SERVER_DB_URL }}
MASTER_DB_USERNAME=${{ secrets.TEST_SERVER_DB_USERNAME }}
MASTER_DB_PASSWORD=${{ secrets.TEST_SERVER_DB_PASSWORD }}
SLAVE_DB_URL=${{ secrets.SLAVE_DB_URL }}
SLAVE_DB_USERNAME=${{ secrets.SLAVE_DB_USERNAME }}
SLAVE_DB_PASSWORD=${{ secrets.SLAVE_DB_PASSWORD }}
SLAVE_DB_URL=${{ secrets.TEST_SERVER_DB_URL }}
SLAVE_DB_USERNAME=${{ secrets.TEST_SERVER_DB_USERNAME }}
SLAVE_DB_PASSWORD=${{ secrets.TEST_SERVER_DB_PASSWORD }}
DDL_AUTO=${{ secrets.DDL_AUTO }}
# OAUTH & JWT
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: PULL REQUEST
name: pull request

on:
pull_request:
Expand All @@ -11,7 +11,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ vars.SLACK_BRANCH}}
ref: ${{vars.SLACK_BRANCH}}

- name: Install jq
run: sudo apt-get install jq
Expand Down Expand Up @@ -43,7 +43,7 @@ jobs:
id: pull_request_notify
uses: slackapi/[email protected]
with:
channel-id: ${{secrets.SLACK_FE_REVIEW_CHANNEL}}
channel-id: ${{secrets.SLACK_BE_REVIEW_CHANNEL}}
payload: |
{
"text": "⌛PR왔다 리뷰해라⌛",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,19 @@
import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.RequiredArgsConstructor;
import site.coduo.common.config.web.filter.AccessTokenSessionFilter;
import site.coduo.common.config.web.filter.AccessTokenCookieFilter;
import site.coduo.common.config.web.filter.AuthFailHandlerFilter;
import site.coduo.common.config.web.filter.SignInCookieFilter;
import site.coduo.common.config.web.filter.StateSessionFilter;
import site.coduo.member.infrastructure.security.JwtProvider;

@RequiredArgsConstructor
@Configuration
public class FilterConfig {

@Bean
public FilterRegistrationBean<StateSessionFilter> stateSessionFilter() {
final FilterRegistrationBean<StateSessionFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new StateSessionFilter());
bean.addUrlPatterns("/api/github/callback");
bean.setOrder(1);
return bean;
}

@Bean
public FilterRegistrationBean<AccessTokenSessionFilter> accessTokenSessionFilter() {
final FilterRegistrationBean<AccessTokenSessionFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new AccessTokenSessionFilter());
public FilterRegistrationBean<AccessTokenCookieFilter> accessTokenSessionFilter(final JwtProvider jwtProvider) {
final FilterRegistrationBean<AccessTokenCookieFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new AccessTokenCookieFilter(jwtProvider));
bean.addUrlPatterns("/api/sign-up", "/api/sign-in/callback");
bean.setOrder(2);
return bean;
Expand All @@ -39,7 +29,7 @@ public FilterRegistrationBean<AccessTokenSessionFilter> accessTokenSessionFilter
public FilterRegistrationBean<SignInCookieFilter> signInCookieFilter(final JwtProvider jwtProvider) {
final FilterRegistrationBean<SignInCookieFilter> bean = new FilterRegistrationBean<>();
bean.setFilter(new SignInCookieFilter(jwtProvider));
bean.addUrlPatterns("/api/sign-out", "/api/member", "/api/sign-in/check");
bean.addUrlPatterns("/api/sign-out", "/api/member");
bean.setOrder(1);
return bean;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package site.coduo.common.config.web.filter;

import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;

import lombok.RequiredArgsConstructor;
import site.coduo.member.exception.AuthenticationException;
import site.coduo.member.infrastructure.security.JwtProvider;

@RequiredArgsConstructor
public class AccessTokenCookieFilter implements Filter {

public static final String TEMPORARY_ACCESS_TOKEN_COOKIE_NAME = "temp_access";

private final JwtProvider jwtProvider;

@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) request;
if (httpRequest.getMethod().equalsIgnoreCase("OPTIONS")) {
chain.doFilter(request, response);
return;
}
validate(parseSignInCookie(httpRequest));
chain.doFilter(request, response);
}

private Cookie parseSignInCookie(final HttpServletRequest request) {
final Cookie[] cookies = request.getCookies();
if (Objects.isNull(cookies)) {
throw new AuthenticationException("쿠키 값이 비어있습니다.");
}

return Arrays.stream(cookies)
.filter(cookie -> cookie.getName().equals(TEMPORARY_ACCESS_TOKEN_COOKIE_NAME))
.findAny()
.orElseThrow(() -> new AuthenticationException("임시 엑세스 토큰 쿠키를 찾을 수 없습니다."));
}

private void validate(final Cookie cookie) {
if (jwtProvider.isValid(cookie.getValue())) {
return;
}
throw new AuthenticationException("임시 엑세스 토큰 쿠키 값이 유효하지 않습니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
public class AccessTokenSessionFilter implements SessionFilter {

public static final String ACCESS_TOKEN_SESSION_NAME = "access token";
public static final int ACCESS_TOKEN_EXPIRE_IN_SECOND = 600;

@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package site.coduo.member.client;

import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClient.ResponseSpec.ErrorHandler;

import lombok.extern.slf4j.Slf4j;
import site.coduo.member.client.dto.GithubUserRequest;
import site.coduo.member.client.dto.GithubUserResponse;
import site.coduo.member.exception.ExternalApiCallException;

@Slf4j
@Component
Expand All @@ -30,13 +33,29 @@ public GithubApiClient() {
.build();
}

GithubApiClient(final RestClient restClient) {
this.client = restClient;
}

public GithubUserResponse getUser(final GithubUserRequest request) {

return client.get()
.uri("/user")
.accept()
.headers(httpHeaders -> httpHeaders.addAll(request.getHeaders()))
.retrieve()
.onStatus(HttpStatusCode::isError, getErrorHandler())
.body(GithubUserResponse.class);
}

private ErrorHandler getErrorHandler() {
return (request, response) -> {
if (response.getStatusCode().is4xxClientError()) {
throw new ExternalApiCallException("Github API 호출에 실패했습니다.");
}
if (response.getStatusCode().is5xxServerError()) {
throw new ExternalApiCallException("Github API 호출 과정 중 서버 내부에서 오류가 발생했습니다.");
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClient.ResponseSpec.ErrorHandler;

import site.coduo.member.client.dto.TokenRequest;
import site.coduo.member.client.dto.TokenResponse;
import site.coduo.member.exception.ExternalApiCallException;
import site.coduo.member.infrastructure.http.Basic;

@Component
Expand Down Expand Up @@ -50,6 +53,7 @@ public TokenResponse grant(final TokenRequest request) {
.header(HttpHeaders.AUTHORIZATION, new Basic(oAuthClientId, oAuthClientSecret).getValue())
.body(request.toQueryParams())
.retrieve()
.onStatus(HttpStatusCode::isError, getErrorHandler())
.body(TokenResponse.class);
}

Expand All @@ -60,4 +64,15 @@ public String getOAuthClientId() {
public String getOAuthRedirectUri() {
return oAuthRedirectUri;
}

private ErrorHandler getErrorHandler() {
return (request, response) -> {
if (response.getStatusCode().is4xxClientError()) {
throw new ExternalApiCallException("Github OAuth API 호출에 실패했습니다.");
}
if (response.getStatusCode().is5xxServerError()) {
throw new ExternalApiCallException("Github OAuth 과정 중 서버 내부에서 오류가 발생했습니다.");
}
};
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package site.coduo.member.controller;

import static site.coduo.common.config.web.filter.AccessTokenSessionFilter.ACCESS_TOKEN_SESSION_NAME;
import static site.coduo.common.config.web.filter.AccessTokenCookieFilter.TEMPORARY_ACCESS_TOKEN_COOKIE_NAME;
import static site.coduo.common.config.web.filter.SignInCookieFilter.SIGN_IN_COOKIE_NAME;

import java.net.URI;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
Expand All @@ -15,7 +14,6 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.SessionAttribute;

import lombok.RequiredArgsConstructor;
import site.coduo.member.controller.docs.AuthControllerDocs;
Expand All @@ -31,26 +29,24 @@
@RestController
public class AuthController implements AuthControllerDocs {

public static final String PRODUCT_DOMAIN = ".coduo.site";

private final AuthService authService;
private final MemberService memberService;

@Value("${front.url}")
private String frontUrl;

@GetMapping("/sign-out")
public ResponseEntity<Void> signOut(@CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken) {
final SignInCookie cookie = new SignInCookie(signInToken);

final ResponseCookie expire = SignInCookie.expire(PRODUCT_DOMAIN);
return ResponseEntity.ok()
.header(HttpHeaders.SET_COOKIE, cookie.expire(frontUrl).toString())
.header(HttpHeaders.SET_COOKIE, expire.toString())
.build();
}

@PostMapping("/sign-up")
public ResponseEntity<Void> signUp(@RequestBody final SignUpRequest request,
@SessionAttribute(name = ACCESS_TOKEN_SESSION_NAME) final String accessToken
@CookieValue(name = TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) final String encryptedAccessToken
) {
memberService.createMember(request.username(), accessToken);
memberService.createMember(request.username(), encryptedAccessToken);

return ResponseEntity.status(HttpStatus.FOUND)
.location(URI.create("/api/sign-in/callback"))
Expand All @@ -59,19 +55,19 @@ public ResponseEntity<Void> signUp(@RequestBody final SignUpRequest request,

@GetMapping("/sign-in/callback")
public ResponseEntity<SignInWebResponse> signInCallback(
@SessionAttribute(name = ACCESS_TOKEN_SESSION_NAME) final String accessToken
@CookieValue(name = TEMPORARY_ACCESS_TOKEN_COOKIE_NAME) final String encryptedAccessToken
) {
final SignInServiceResponse serviceResponse = authService.createSignInToken(accessToken);
final ResponseCookie cookie = new SignInCookie(serviceResponse.token()).generate(frontUrl);
final SignInServiceResponse serviceResponse = authService.createSignInToken(encryptedAccessToken);
final ResponseCookie signInCookie = new SignInCookie(serviceResponse.token()).generate(PRODUCT_DOMAIN);

return ResponseEntity.ok()
.header(HttpHeaders.SET_COOKIE, cookie.toString())
.header(HttpHeaders.SET_COOKIE, signInCookie.toString())
.body(SignInWebResponse.of(serviceResponse));
}

@GetMapping("/sign-in/check")
public ResponseEntity<SignInCheckResponse> signInCheck(
@CookieValue(name = SIGN_IN_COOKIE_NAME) final String signInToken
@CookieValue(name = SIGN_IN_COOKIE_NAME, required = false) final String signInToken
) {
final boolean signedIn = authService.isSignedIn(signInToken);
final SignInCheckResponse response = new SignInCheckResponse(signedIn);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
package site.coduo.member.controller;


import static site.coduo.common.config.web.filter.AccessTokenSessionFilter.ACCESS_TOKEN_EXPIRE_IN_SECOND;
import static site.coduo.common.config.web.filter.AccessTokenSessionFilter.ACCESS_TOKEN_SESSION_NAME;
import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_EXPIRE_IN_SECOND;
import static site.coduo.common.config.web.filter.StateSessionFilter.STATE_SESSION_NAME;
import static site.coduo.member.controller.AuthController.PRODUCT_DOMAIN;

import java.net.URI;

import jakarta.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import site.coduo.member.client.dto.TokenResponse;
import site.coduo.member.controller.docs.GithubOAuthControllerDocs;
import site.coduo.member.service.GithubOAuthService;
import site.coduo.member.service.dto.auth.AccessTokenCookie;
import site.coduo.member.service.dto.oauth.GithubAuthQuery;
import site.coduo.member.service.dto.oauth.GithubAuthUri;
import site.coduo.member.service.dto.oauth.GithubCallbackQuery;
Expand Down Expand Up @@ -51,12 +52,12 @@ public ResponseEntity<GithubOAuthEndpoint> getGithubAuthCode(final HttpSession s
@GetMapping("/github/callback")
public ResponseEntity<Void> getAccessToken(@ModelAttribute final GithubCallbackQuery query,
final HttpSession session) {
final TokenResponse tokenResponse = githubOAuthService.invokeOAuthCallback(query.code());
final String encryptedAccessToken = githubOAuthService.invokeOAuthCallback(query.code());
final AccessTokenCookie cookie = new AccessTokenCookie(encryptedAccessToken);
final ResponseCookie responseCookie = cookie.generate(PRODUCT_DOMAIN);

session.setAttribute(ACCESS_TOKEN_SESSION_NAME, tokenResponse.accessToken());
session.setMaxInactiveInterval(ACCESS_TOKEN_EXPIRE_IN_SECOND);

return ResponseEntity.status(HttpStatus.FOUND)
return ResponseEntity.status(HttpStatus.TEMPORARY_REDIRECT)
.header(HttpHeaders.SET_COOKIE, responseCookie.toString())
.location(URI.create("https://" + frontUrl + "/callback"))
.build();
}
Expand Down
Loading

0 comments on commit f88aba2

Please sign in to comment.