프로젝트 코드 리뷰
package com.project.erpre.config;
import com.project.erpre.auth.CustomUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
import java.util.Collections;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
private final CustomUserDetailsService customUserDetailsService;
public WebSecurityConfig(CustomUserDetailsService customUserDetailsService) {
this.customUserDetailsService = customUserDetailsService;
}
// 회원가입이 생략되어 평문 비밀번호를 사용하고 있으므로 BCryptPasswordEncoder 제거
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
// configuration.setAllowedOriginPatterns(Collections.singletonList("http://localhost:8787")); // 웹소켓에서는 allowedOrigins 대신 allowedOriginPatterns 사용
configuration.setAllowedOriginPatterns(Collections.singletonList("*")); // 웹소켓에서는 allowedOrigins 대신 allowedOriginPatterns 사용
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); // 모든 HTTP 메서드 명시적 허용
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type")); // 필요한 헤더 추가
configuration.setAllowCredentials(true); // 쿠키 허용 // test 환경에서 false
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.cors().configurationSource(corsConfigurationSource()) // CORS 설정 활성화 및 직접 설정
.and()
.csrf()
.ignoringAntMatchers("/talk/**", "/app/**", "/topic/**", "/ws/**", "/queue/**")
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS) // 세션 기반으로 설정
.and()
.authorizeRequests()
.antMatchers("/orderReport", "/employeeAttend", "/employeeSalary").hasAuthority("ROLE_SPECIAL_ACCESS") // 특정 페이지 접근 제한
.antMatchers("/android/api/**").permitAll()
.antMatchers("/**", "/api/**", "/talk/**", "/user/**", "/app/**", "/topic/**", "/ws/**", "/queue/**", "/uploads/**", "/profile-pictures/**", "/chat/**", "/Temp/**").permitAll()
.antMatchers("/", "/static/**", "/bundle/**", "/img/**", "/css/**", "/fonts/**", "/index.html").permitAll()
.antMatchers("/api/login", "/login").permitAll() // 로그인 앤드포인트 허용 (현재 모든 페이지 접근 허용! 이거 나중에 바꿔야 함)
.antMatchers("/user/**", "/").hasAnyRole("Staff", "Admin", "Assistant Manager", "Executive", "Director", "Manager")
.antMatchers("/admin/**").hasRole("Admin")
.anyRequest().permitAll() // 인증 필요 없음
.and()
.formLogin() // 기본 로그인 폼 제공
.loginPage("/login")
.defaultSuccessUrl("/main", true)
.permitAll()
.and()
.logout() // 로그아웃 처리
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll();
// .and()
// .anonymous().disable(); // 익명 사용자 비활성화
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class).build();
}
}
이 코드는 Spring Security를 사용하여 웹 애플리케이션의 보안 설정을 정의하는 설정 파일임. WebSecurityConfig 클래스는 사용자 인증 및 인가, CORS 설정, CSRF 보호 해제, 로그인 및 로그아웃 설정 등을 담당함.
내가 했던 부분은 아니지만 공부차원에서 코드 리뷰를 해보려고 한다🫡

1. 클래스 및 생성자
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {
private final CustomUserDetailsService customUserDetailsService;
public WebSecurityConfig(CustomUserDetailsService customUserDetailsService) {
this.customUserDetailsService = customUserDetailsService;
}
}
설명
- @Configuration : 이 클래스가 Spring 설정 파일임을 나타냅니다.
- @EnableWebSecurity : Spring Security 기능을 활성화하는 어노테이션입니다.
- CustomUserDetailsService customUserDetailsService :
- CustomUserDetailsService는 사용자 인증을 처리하는 서비스입니다.
- UserDetailsService 인터페이스를 구현한 클래스이며, DB에서 사용자 정보를 불러오는 역할을 합니다.
관련 개념
- UserDetailsService는 Spring Security에서 사용자 정보를 관리하는 핵심 인터페이스입니다.
- CustomUserDetailsService는 DB에서 사용자 계정을 조회하고 인증을 수행하는 역할을 합니다.
2. 비밀번호 인코더 설정
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
설명
- @Bean : 이 메서드는 Spring Bean으로 등록되어 애플리케이션에서 재사용됩니다.
- NoOpPasswordEncoder.getInstance() :
- 평문(암호화되지 않은) 비밀번호를 사용합니다. 보안상 매우 위험하므로, 실무에서는 BCryptPasswordEncoder를 사용해야 합니다.
관련 개념
- PasswordEncoder : 비밀번호를 해싱(암호화)하는 인터페이스
- NoOpPasswordEncoder : 비밀번호를 암호화하지 않고 그대로 저장하는 방식 (안전하지 않음!)
- BCryptPasswordEncoder : 보안성을 위해 가장 널리 사용되는 비밀번호 인코더
주의
실제 프로젝트에서는 NoOpPasswordEncoder를 절대 사용하면 안 됩니다. 반드시 BCryptPasswordEncoder를 사용하세요.
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
3. CORS 설정 (Cross-Origin Resource Sharing)
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(Collections.singletonList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
설명
- CORS(Cross-Origin Resource Sharing)는 다른 도메인에서 API 요청을 허용하는 기능입니다.
- setAllowedOriginPatterns("*") : 모든 도메인에서 요청을 허용 (*).
- setAllowedMethods(...) : GET, POST, PUT, DELETE, OPTIONS 요청을 허용.
- setAllowedHeaders(...) : 특정 HTTP 헤더(Authorization, Cache-Control, Content-Type) 허용.
- setAllowCredentials(true) : 쿠키 기반 인증을 허용.
관련 개념
- CORS란? 웹 브라우저가 다른 도메인에서 요청을 보낼 때 보안 정책을 관리하는 기능
- OPTIONS 메서드: CORS 요청 시 사전 검사 요청(Preflight Request) 역할을 합니다.
주의
실제 운영 환경에서는 특정 도메인만 허용하는 것이 보안상 안전합니다.
configuration.setAllowedOriginPatterns(Collections.singletonList("https://example.com"));
4. Spring Security 필터 체인 (SecurityFilterChain)
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.cors().configurationSource(corsConfigurationSource()) // CORS 설정 적용
.and()
.csrf()
.ignoringAntMatchers("/talk/**", "/app/**", "/topic/**", "/ws/**", "/queue/**")
.disable()
설명
- http.cors() : 위에서 설정한 CORS 정책을 적용합니다.
- .csrf().disable() : CSRF 보호를 비활성화합니다.
- .ignoringAntMatchers(...) :
- 특정 경로(/talk/**, /app/**, /topic/**, /ws/**, /queue/**)에 대해서는 CSRF 보호를 해제합니다.
관련 개념
- CSRF(Cross-Site Request Forgery) : 악성 웹사이트가 사용자의 세션을 도용하는 공격 기법
- CSRF 보호 해제 시 주의할 점: 보안이 필요한 POST 요청에서는 CSRF 보호를 유지하는 것이 안전.
5. 세션 설정
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
설명
- SessionCreationPolicy.ALWAYS : 항상 세션을 생성하여 사용자 정보를 저장합니다.
관련 개념
- SessionCreationPolicy.ALWAYS : 항상 세션을 생성 (기본값)
- SessionCreationPolicy.STATELESS : 세션을 사용하지 않음 (JWT 기반 인증에서 사용)
6. 접근 권한 설정 (인가 - Authorization)
.authorizeRequests()
.antMatchers("/orderReport", "/employeeAttend", "/employeeSalary").hasAuthority("ROLE_SPECIAL_ACCESS")
.antMatchers("/android/api/**").permitAll()
.antMatchers("/api/login", "/login").permitAll()
.antMatchers("/user/**", "/").hasAnyRole("Staff", "Admin", "Assistant Manager", "Executive", "Director", "Manager")
.antMatchers("/admin/**").hasRole("Admin")
.anyRequest().permitAll()
설명
- .hasAuthority("ROLE_SPECIAL_ACCESS") : 특정 페이지 접근 제한 (예: ROLE_SPECIAL_ACCESS 권한을 가진 사용자만 접근 가능)
- .antMatchers("/android/api/**").permitAll() : Android API는 누구나 접근 가능.
- .hasAnyRole(...) : 특정 역할(Role)을 가진 사용자만 접근 가능.
- .hasRole("Admin") : 관리자만 접근 가능.
관련 개념
- 인증(Authentication): 사용자가 로그인했는지 확인 (예: ID/PW 입력)
- 인가(Authorization): 사용자가 특정 기능을 사용할 수 있는지 확인 (예: 관리자 권한 체크)
7. 로그인 & 로그아웃 설정
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/main", true)
.permitAll()
loginPage("/login") : 로그인 페이지 경로 설정
defaultSuccessUrl("/main", true) : 로그인 성공 시 이동할 페이지
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll();
logoutUrl("/logout") : 로그아웃 요청 경로
logoutSuccessUrl("/login") : 로그아웃 후 이동할 페이지
관련 개념
- Spring Security는 기본적으로 /login과 /logout 엔드포인트를 제공함
- 커스텀 로그인 페이지를 사용하려면 .loginPage("/custom-login") 으로 변경 가능
8. 인증 관리자 설정
@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class).build();
}
설명
AuthenticationManager는 사용자 인증을 처리하는 핵심 객체입니다.
결론
이 설정 파일은 Spring Security를 사용하여 인증 및 인가, CORS 설정, CSRF 보호, 로그인/로그아웃 기능을 정의합니다.
🚨 배포 전에 반드시 확인해야 할 보안 설정:
- NoOpPasswordEncoder 대신 BCryptPasswordEncoder 사용
- permitAll()을 제거하고 적절한 권한 설정 적용
- SessionCreationPolicy 설정을 환경에 맞게 조정 (예: JWT 사용 시 STATELESS로 변경)
'개발인생 > Project' 카테고리의 다른 글
| 페이징처리 프로젝트 구현(Oracle) (0) | 2025.03.17 |
|---|---|
| 페이징처리 프로젝트 구현(React) (0) | 2025.03.17 |
| 페이징처리 - Pagination.js + Pageable (0) | 2025.03.17 |
| 프로젝트 코드 리뷰: JPQL vs QueryDSL 최적화 및 리팩토링 (0) | 2025.02.21 |
| JSP/Servlet 프로젝트와 React+Spring Boot 프로젝트 비교 (1) | 2025.02.19 |