Skip to content

Commit

Permalink
chore: 프로파일 분리 및 운영환경 유틸리티 관련 기능 추가 (#36)
Browse files Browse the repository at this point in the history
* chore: yaml 파일 프로파일에 따라 분리

* feat: 운영환경 체크 유틸리티 구현

* feat: 운영환경에 따른 CORS 설정 추가

* refactor: HttpSecurity 변수명 변경

* refactor: 운영환경 유틸리티 오타 수정

* feat: 운영환경에 따라 SameSite 정책 다르게 설정

* refactor: 응답 객체를 리턴하지 않도록 수정

* refactor: 스프링부트 쿠키 상수 사용하도록 변경

* refactor: 상수 클래스 인스턴스화 막기
  • Loading branch information
uwoobeat authored Jan 31, 2024
1 parent 8dcf2e1 commit b9bce54
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.gdschongik.gdsc.global.common.constant;

import java.util.List;

public class EnvironmentConstant {

private EnvironmentConstant() {}

public static final String PROD = "prod";
public static final String DEV = "dev";
public static final String LOCAL = "local";
public static final List<String> PROD_AND_DEV = List.of(PROD, DEV);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.gdschongik.gdsc.global.common.constant;

public class UrlConstant {

private UrlConstant() {}

public static final String PROD_CLIENT_URL = "https://onboarding.gdschongik.com";
public static final String DEV_CLIENT_URL = "https://dev-onboarding.gdschongik.com";
public static final String LOCAL_REACT_CLIENT_URL = "http://localhost:3000";
public static final String LOCAL_VITE_CLIENT_URL = "http://localhost:5173";
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.gdschongik.gdsc.global.config;

import static com.gdschongik.gdsc.global.common.constant.UrlConstant.*;
import static org.springframework.http.HttpHeaders.*;
import static org.springframework.security.config.Customizer.*;

import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -10,6 +12,7 @@
import com.gdschongik.gdsc.global.security.JwtExceptionFilter;
import com.gdschongik.gdsc.global.security.JwtFilter;
import com.gdschongik.gdsc.global.util.CookieUtil;
import com.gdschongik.gdsc.global.util.EnvironmentUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -19,6 +22,9 @@
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
@EnableWebSecurity
Expand All @@ -29,24 +35,24 @@ public class WebSecurityConfig {
private final JwtService jwtService;
private final CookieUtil cookieUtil;
private final ObjectMapper objectMapper;
private final EnvironmentUtil environmentUtil;

@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.httpBasic(AbstractHttpConfigurer::disable)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.httpBasic(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable)
.cors(withDefaults())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

httpSecurity.oauth2Login(
http.oauth2Login(
oauth2 -> oauth2.userInfoEndpoint(userInfo -> userInfo.userService(customUserService(memberRepository)))
.successHandler(customSuccessHandler(jwtService, cookieUtil)));

httpSecurity.addFilterBefore(jwtFilter(jwtService, cookieUtil), UsernamePasswordAuthenticationFilter.class);
httpSecurity.addFilterBefore(jwtExceptionFilter(objectMapper), JwtFilter.class);
http.addFilterBefore(jwtFilter(jwtService, cookieUtil), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(jwtExceptionFilter(objectMapper), JwtFilter.class);

return httpSecurity.build();
return http.build();
}

@Bean
Expand All @@ -68,4 +74,28 @@ public JwtFilter jwtFilter(JwtService jwtService, CookieUtil cookieUtil) {
public JwtExceptionFilter jwtExceptionFilter(ObjectMapper objectMapper) {
return new JwtExceptionFilter(objectMapper);
}

@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();

if (environmentUtil.isProdProfile()) {
configuration.addAllowedOriginPattern(PROD_CLIENT_URL);
}

if (environmentUtil.isDevProfile()) {
configuration.addAllowedOriginPattern(DEV_CLIENT_URL);
configuration.addAllowedOriginPattern(LOCAL_REACT_CLIENT_URL);
configuration.addAllowedOriginPattern(LOCAL_VITE_CLIENT_URL);
}

configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
configuration.setAllowCredentials(true);
configuration.addExposedHeader(SET_COOKIE);

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
20 changes: 15 additions & 5 deletions src/main/java/com/gdschongik/gdsc/global/util/CookieUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@

import com.gdschongik.gdsc.global.common.constant.JwtConstant;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.web.server.Cookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class CookieUtil {

public HttpServletResponse addTokenCookies(HttpServletResponse response, String accessToken, String refreshToken) {
// TODO: Prod profile일 때는 Strict, 아니면 None으로 설정
String sameSite = "None";
private final EnvironmentUtil environmentUtil;

public void addTokenCookies(HttpServletResponse response, String accessToken, String refreshToken) {

String sameSite = determineSameSitePolicy();

ResponseCookie accessTokenCookie =
generateCookie(JwtConstant.ACCESS_TOKEN.getCookieName(), accessToken, sameSite);
Expand All @@ -21,8 +26,6 @@ public HttpServletResponse addTokenCookies(HttpServletResponse response, String

response.addHeader(HttpHeaders.SET_COOKIE, accessTokenCookie.toString());
response.addHeader(HttpHeaders.SET_COOKIE, refreshTokenCookie.toString());

return response;
}

private ResponseCookie generateCookie(String cookieName, String tokenValue, String sameSite) {
Expand All @@ -33,4 +36,11 @@ private ResponseCookie generateCookie(String cookieName, String tokenValue, Stri
.httpOnly(false)
.build();
}

private String determineSameSitePolicy() {
if (environmentUtil.isProdProfile()) {
return Cookie.SameSite.STRICT.attributeValue();
}
return Cookie.SameSite.NONE.attributeValue();
}
}
38 changes: 38 additions & 0 deletions src/main/java/com/gdschongik/gdsc/global/util/EnvironmentUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.gdschongik.gdsc.global.util;

import static com.gdschongik.gdsc.global.common.constant.EnvironmentConstant.*;

import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class EnvironmentUtil {

private final Environment environment;

public String getCurrentProfile() {
return getActiveProfiles()
.filter(profile -> profile.equals(PROD) || profile.equals(DEV))
.findFirst()
.orElse(LOCAL);
}

public Boolean isProdProfile() {
return getActiveProfiles().anyMatch(PROD::equals);
}

public Boolean isDevProfile() {
return getActiveProfiles().anyMatch(DEV::equals);
}

public Boolean isProdAndDevProfile() {
return getActiveProfiles().anyMatch(PROD_AND_DEV::contains);
}

private Stream<String> getActiveProfiles() {
return Stream.of(environment.getActiveProfiles());
}
}
12 changes: 12 additions & 0 deletions src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
spring:
config:
activate:
on-profile: "dev"
jpa:
hibernate:
ddl-auto: update

logging:
level:
org.springframework.orm.jpa: DEBUG
org.springframework.transaction: DEBUG
19 changes: 19 additions & 0 deletions src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
spring:
config:
activate:
on-profile: "local"

jpa:
hibernate:
ddl-auto: update
show-sql: ${SHOW_SQL:true}
properties:
hibernate:
format_sql: ${FORMAT_SQL:true}
defer-datasource-initialization: true
open-in-view: false

logging:
level:
org.springframework.orm.jpa: DEBUG
org.springframework.transaction: DEBUG
10 changes: 10 additions & 0 deletions src/main/resources/application-redis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
spring:
config:
activate:
on-profile: "redis"

data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
22 changes: 22 additions & 0 deletions src/main/resources/application-security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
spring:
config:
activate:
on-profile: "security"

security:
oauth2:
client:
registration:
github:
client-id: ${GITHUB_CLIENT_ID:default}
client-secret: ${GITHUB_CLIENT_SECRET:default}

jwt:
token:
ACCESS_TOKEN:
secret: ${JWT_ACCESS_TOKEN_SECRET:}
expiration-time: ${JWT_ACCESS_TOKEN_EXPIRATION_TIME:7200}
REFRESH_TOKEN:
secret: ${JWT_REFRESH_TOKEN_SECRET:}
expiration-time: ${JWT_REFRESH_TOKEN_EXPIRATION_TIME:604800}
issuer: ${JWT_ISSUER:}
34 changes: 10 additions & 24 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,14 @@
spring:
profiles:
group:
local: "datasource"
dev: "datasource"
test: "test"
security:
oauth2:
client:
registration:
github:
client-id: ${GITHUB_CLIENT_ID:default}
client-secret: ${GITHUB_CLIENT_SECRET:default}
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
test: "test"
local: "local, datasource"
dev: "dev, datasource"
include:
- redis
- storage
- security

jwt:
token:
ACCESS_TOKEN:
secret: ${JWT_ACCESS_TOKEN_SECRET:}
expiration-time: ${JWT_ACCESS_TOKEN_EXPIRATION_TIME:7200}
REFRESH_TOKEN:
secret: ${JWT_REFRESH_TOKEN_SECRET:}
expiration-time: ${JWT_REFRESH_TOKEN_EXPIRATION_TIME:604800}
issuer: ${JWT_ISSUER:}
logging:
level:
com.gdschongik.gdsc.domain.*.api.*: debug

0 comments on commit b9bce54

Please sign in to comment.