Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: 프로파일 분리 및 운영환경 유틸리티 관련 기능 추가 #36

Merged
merged 9 commits into from
Jan 31, 2024
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())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 수정 필요없나요?
.cors().configurationSource(corsConfigurationSource()) 가 되어야 하는게 아닌가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.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();
}
}
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
Loading