Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
267f408
refactor: 인증과정 예외처리 #55
zhy2on Aug 1, 2024
4455052
chore: 테스트용 cors 설정
zhy2on Aug 1, 2024
c1df516
fix: 인증과정 오류 수정
zhy2on Aug 2, 2024
a1e8a1f
Merge pull request #56 from tutorial-sejong/refactor/55
zhy2on Aug 3, 2024
d232a5b
refactor: 수강신청 관련 api 리팩토링 (#59)
zhy2on Aug 3, 2024
60fa1d5
refactor: 강좌 조회 api 수정
zhy2on Aug 3, 2024
7695308
fix: 수강 신청 동시성 이슈 해결 및 코드 개선 (#61)
zhy2on Aug 5, 2024
13b7cb5
chore: submodule update
stableh Aug 5, 2024
bc42dcb
chore: submodule update
stableh Aug 5, 2024
87353db
feat: 유저정보 제거 로직 추가 (#65)
stableh Aug 8, 2024
f1b4ffe
feat: 동일한 학수번호 예외처리 (#70)
zhy2on Aug 9, 2024
68217a8
refactor: 매크로 코드 업데이트 (#72)
stableh Aug 9, 2024
23b9e63
Refactor/71 anti macro image update and scale (#74)
stableh Aug 12, 2024
7b28a45
TS-18: exception, response 리팩토링 (#76)
zhy2on Sep 8, 2024
4636cd3
refactor(TS-21): JWT 인증 예외 처리 개선 (#77)
zhy2on Sep 11, 2024
07216bc
feat: config 수정 (#82)
zhy2on Jan 6, 2025
3d19a43
merge conflict
zhy2on Jan 6, 2025
ddf1dd1
feat(TS-5): Logback 설정 추가 및 기본 로깅 구성 완료 (#83)
stableh Jan 7, 2025
37e9730
chore: submodule Update
stableh Jan 7, 2025
0abce1c
chore: submodule Update (#84)
stableh Jan 7, 2025
0cfd236
Ts 5/chore/logback setup (#85)
stableh Jan 7, 2025
f6d8bac
refactor(TS-35): 매크로 방지 이미지 생성을 Simple Captcha를 이용하도록 변경 (#86)
zhy2on Jan 8, 2025
836d09f
chore: allowed-origins 추가
zhy2on Jan 19, 2025
d8ef5ef
feat(TS-48): 인기 강좌 목록 조회 기능 구현 (#87)
zhy2on Jan 19, 2025
a86ad3f
refactor: CORS 설정을 application.properties로 분리 (#88)
zhy2on Jan 19, 2025
e857b13
chore(TS-52): swagger 설정 (#89)
stableh Jan 20, 2025
eb375d9
refactor: 에러코드 네이밍 수정 (#92)
zhy2on Jan 24, 2025
c190e9a
fix: 토큰 만료 에러 처리 시의 로깅 로직 수정 (#90)
zhy2on Jan 24, 2025
2319614
chore: 테스트용 토큰 만료 시간 변경
zhy2on Jan 24, 2025
4c32110
chore: 토큰 만료 시간 변경
zhy2on Jan 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,39 @@ jobs:
- name: Grant execute permission for gradlew
run: chmod +x gradlew

# --- (1) build 시 테스트 무시: -x test ---
- name: Build with Gradle
run: ./gradlew build -x test

- name: Install sshpass
run: sudo apt-get install -y sshpass

# 빌드 산출물(JAR 파일)을 원격 서버로 복사
- name: Copy build artifacts
run: |
JAR_FILE=$(ls build/libs/*.jar | sort -r | head -n 1)
echo "JAR_FILE=$JAR_FILE" >> $GITHUB_ENV
sshpass -p ${{ secrets.SSH_PASSWORD }} scp -o StrictHostKeyChecking=no -r $JAR_FILE ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/home/anhye0n/web/tutorial_sejong/backend/
sshpass -p ${{ secrets.SSH_PASSWORD }} scp -o StrictHostKeyChecking=no -r "$JAR_FILE" ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/home/anhye0n/web/tutorial_sejong/backend/

# --- (2) systemd daemon-reload 추가 후 서비스 재시작 ---
- name: Restart backend service
run: |
sshpass -p ${{ secrets.SSH_PASSWORD }} ssh -o StrictHostKeyChecking=no ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} << EOF
cd /home/anhye0n/web/tutorial_sejong/backend/
git submodule update --init --recursive

CURRENT_PID=\$(lsof -t -i:8080)
if [ -n "\$CURRENT_PID" ]; then
echo "Stopping process using port 8080 with PID \$CURRENT_PID"
echo ${{ secrets.SSH_PASSWORD }} | sudo -S kill -9 \$CURRENT_PID
fi

# systemd 설정 변경/추가 후 재로딩
echo ${{ secrets.SSH_PASSWORD }} | sudo -S systemctl daemon-reload
echo ${{ secrets.SSH_PASSWORD }} | sudo -S systemctl restart tutorial_sejong_backend
echo ${{ secrets.SSH_PASSWORD }} | sudo -S systemctl status tutorial_sejong_backend

# DB 스크립트 실행 (15초 대기 후)
sleep 15
mysql -u ${{ secrets.MARIADB_ID }} -p${{ secrets.MARIADB_PASSWORD }} < /home/anhye0n/web/tutorial_sejong/backend/tutorial_sejong.sql
EOF
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ out/

application*.properties
src/main/resources/config

logs/*
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'org.projectlombok:lombok'
implementation 'cn.apiclub.tool:simplecaptcha:1.2.2'

annotationProcessor 'org.projectlombok:lombok'

runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'io.rest-assured:rest-assured:5.4.0'
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.tutorialsejong.courseregistration.common.config;

import cn.apiclub.captcha.backgrounds.BackgroundProducer;
import cn.apiclub.captcha.backgrounds.GradiatedBackgroundProducer;
import cn.apiclub.captcha.text.renderer.DefaultWordRenderer;
import cn.apiclub.captcha.text.renderer.WordRenderer;
import java.awt.Color;
import java.awt.Font;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CaptchaConfig {

@Bean
public WordRenderer wordRenderer() {
return new DefaultWordRenderer(
List.of(new Color(0, 0, 0)),
List.of(new Font("Helvetica", Font.PLAIN, 60))
);
}

@Bean
public BackgroundProducer backgroundProducer() {
GradiatedBackgroundProducer producer = new GradiatedBackgroundProducer();
producer.setFromColor(new Color(100, 100, 100));
producer.setToColor(new Color(180, 180, 180));
return producer;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.tutorialsejong.courseregistration.common.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {

@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}
Original file line number Diff line number Diff line change
@@ -1,68 +1,119 @@
package com.tutorialsejong.courseregistration.common.config;

import com.tutorialsejong.courseregistration.auth.JwtAuthenticationFilter;
import com.tutorialsejong.courseregistration.auth.JwtTokenProvider;
import com.tutorialsejong.courseregistration.common.security.JwtAuthenticationEntryPoint;
import com.tutorialsejong.courseregistration.common.security.JwtAuthenticationFilter;
import com.tutorialsejong.courseregistration.common.security.JwtExceptionFilter;
import com.tutorialsejong.courseregistration.common.security.JwtTokenProvider;
import com.tutorialsejong.courseregistration.common.security.SwaggerAccessDeniedHandler;
import com.tutorialsejong.courseregistration.common.security.SwaggerAuthenticationEntryPoint;
import java.util.Arrays;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Value("${security.cors.allowed-origins}")
private String[] allowedOrigins;

@Value("${security.cors.allow-credentials}")
private boolean allowCredentials;

@Value("${app.swagger.user.name}")
private String swaggerUsername;

@Value("${app.swagger.user.password}")
private String swaggerPassword;

private final JwtTokenProvider tokenProvider;

public SecurityConfig(JwtTokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
@Bean
@Order(1)
public SecurityFilterChain swaggerFilterChain(HttpSecurity http, PasswordEncoder passwordEncoder) throws Exception {
// 1) Swagger용 인메모리 사용자 생성
UserDetails admin = User.withUsername(swaggerUsername)
.password(passwordEncoder.encode(swaggerPassword))
.roles("ADMIN")
.build();

InMemoryUserDetailsManager swaggerUserManager = new InMemoryUserDetailsManager(admin);

// 2) Swagger 경로에 대해서만 Basic Auth 설정
http
.securityMatcher("/docs/swagger-ui/**", "/v3/api-docs/**")
.csrf(AbstractHttpConfigurer::disable)
.httpBasic(Customizer.withDefaults())
.userDetailsService(swaggerUserManager)
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(new SwaggerAuthenticationEntryPoint()) // 401
.accessDeniedHandler(new SwaggerAccessDeniedHandler()) // 403
);

return http.build();
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
@Order(2)
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.headers(headers -> headers
.contentSecurityPolicy(csp -> csp
.policyDirectives("frame-ancestors 'self'"))
.contentSecurityPolicy(csp -> csp.policyDirectives("frame-ancestors 'self'"))
)
.cors(cors -> cors
.configurationSource(request -> {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Arrays.asList("http://localhost:3000")); // 변경
config.setAllowedOrigins(Arrays.asList(allowedOrigins));
config.setAllowCredentials(allowCredentials);
config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(Arrays.asList("*"));
config.setAllowCredentials(true);
return config;
})
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/api/auth/login",
"/api/auth/register",
"/api/auth/refresh"
"/api/auth/refresh",
"/api/auth/withdrawal/**"
).permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(new JwtAuthenticationFilter(tokenProvider),
UsernamePasswordAuthenticationFilter.class);

// JWT 기반 인증 필터 적용
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(new JwtAuthenticationEntryPoint())
)
.addFilterBefore(new JwtAuthenticationFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JwtExceptionFilter(), JwtAuthenticationFilter.class);
return http.build();
}

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)
throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}

Expand Down
Loading
Loading