https://docs.spring.io/spring-security/reference/servlet/integrations/cors.html
CORS :: Spring Security
Spring Framework provides first class support for CORS. CORS must be processed before Spring Security, because the pre-flight request does not contain any cookies (that is, the JSESSIONID). If the request does not contain any cookies and Spring Security is
docs.spring.io
세션 정보 확인
@GetMapping("/test")
public String test() {
// 현재 SecurityContext에서 인증된 사용자의 이름을 가져옵니다.
String name = SecurityContextHolder.getContext().getAuthentication().getName();
// 현재 SecurityContext에서 Authentication 객체를 가져옵니다.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 인증된 사용자의 권한(역할) 목록을 가져옵니다.
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
// 권한 목록에서 첫 번째 권한을 가져옵니다.
Iterator<? extends GrantedAuthority> iter = authorities.iterator();
GrantedAuthority auth = iter.next();
// 첫 번째 권한의 이름(역할)을 가져옵니다.
String role = auth.getAuthority();
// 인증된 사용자의 이름과 역할을 포함한 문자열을 반환합니다.
return "test security ContextName : " + name + " " + role;
}
세션 정보 확인 @AuthenticationPricipal
@AuthenticationPrincipal 어노테이션을 사용하면 SecurityContext에서 현재 인증된 사용자의 정보를 주입받을 수 있습니다.
@GetMapping("/test")
public String test(@AuthenticationPrincipal UserDetails userDetails) {
// 인증된 사용자의 이름을 가져옵니다.
String name = userDetails.getUsername();
// 인증된 사용자의 권한(역할) 목록을 가져옵니다.
Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
// 권한 목록에서 첫 번째 권한을 가져옵니다.
Iterator<? extends GrantedAuthority> iter = authorities.iterator();
GrantedAuthority auth = iter.next();
// 첫 번째 권한의 이름(역할)을 가져옵니다.
String role = auth.getAuthority();
// 인증된 사용자의 이름과 역할을 포함한 문자열을 반환합니다.
return "test security ContextName : " + name + " " + role;
}
SecurityConfig
http
.cors((corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() {
// CORS 설정을 위한 configurationSource를 커스터마이징합니다.
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
// getCorsConfiguration 메서드를 오버라이드하여 CORS 설정을 구성합니다.
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
// "http://localhost:3000" 도메인에서 오는 요청을 허용합니다.
configuration.setAllowedMethods(Collections.singletonList("*"));
// 모든 HTTP 메서드를 허용합니다.
configuration.setAllowCredentials(true);
// 자격 증명(쿠키, Authorization 헤더 등)을 포함한 요청을 허용합니다.
configuration.setAllowedHeaders(Collections.singletonList("*"));
// 모든 요청 헤더를 허용합니다.
configuration.setMaxAge(3600L);
// CORS 요청의 사전 검사(pre-flight) 응답을 캐시할 시간(초)을 설정합니다. 여기서는 1시간(3600초)입니다.
configuration.setExposedHeaders(Collections.singletonList("Authorization"));
// "Authorization" 헤더를 클라이언트에 노출합니다.
return configuration;
}
})));
// 이 설정을 통해 모든 경로에 대해 CORS 설정을 적용합니다.
이 설정은 HTTP 요청에 대한 CORS 설정을 커스터마이징하는 방법을 보여줍니다. CorsConfigurationSource 인터페이스를 구현하여 getCorsConfiguration 메서드를 오버라이드하고, 여러 CORS 규칙을 설정합니다. 이 예제에서는 http://localhost:3000에서 오는 요청을 허용하며, 모든 HTTP 메서드와 헤더를 허용하고, 자격 증명을 포함한 요청을 허용합니다. 또한, "Authorization" 헤더를 클라이언트에 노출하고, 사전 검사 요청의 응답을 1시간 동안 캐시합니다.
config > CorsMvcConfig
@Configuration // 이 클래스가 스프링의 설정 클래스임을 나타냅니다.
public class CorsMvcConfig implements WebMvcConfigurer { // WebMvcConfigurer를 구현하여 CORS 설정을 추가합니다.
@Override
public void addCorsMappings(CorsRegistry corsRegistry) {
// addCorsMappings 메서드를 오버라이드하여 CORS 설정을 추가합니다.
corsRegistry.addMapping("/**")
// 모든 경로에 대해 CORS 설정을 적용합니다.
.allowedOrigins("http://localhost:3000");
// http://localhost:3000 도메인에서 오는 요청을 허용합니다.
}
}
스프링에서 CORS (Cross-Origin Resource Sharing) 설정을 구성하는 데 사용됩니다. WebMvcConfigurer 인터페이스를 구현하고, addCorsMappings 메서드를 오버라이드하여 특정 도메인에서 오는 요청을 허용합니다.
SecurityConfig 전체 코드
package com.example.loans_domain.auth.config;
import com.example.loans_domain.auth.jwt.JWTFilter;
import com.example.loans_domain.auth.jwt.JWTUtil;
import com.example.loans_domain.auth.jwt.LoginFilter;
import jakarta.servlet.http.HttpServletRequest;
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.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.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
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 java.util.Collections;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final AuthenticationConfiguration authenticationConfiguration;
private final JWTUtil jwtUtil;
public SecurityConfig(AuthenticationConfiguration authenticationConfiguration, JWTUtil jwtUtil) {
this.authenticationConfiguration = authenticationConfiguration;
this.jwtUtil = jwtUtil;
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors((corsCustomizer -> corsCustomizer.configurationSource(new CorsConfigurationSource() {
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("http://localhost:3000"));
configuration.setAllowedMethods(Collections.singletonList("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Collections.singletonList("*"));
configuration.setMaxAge(3600L);
configuration.setExposedHeaders(Collections.singletonList("Authorization"));
return configuration;
}
})));
//csrf disable
http
.csrf((auth) -> auth.disable());
//From 로그인 방식 disable
http
.formLogin((auth) -> auth.disable());
//http basic 인증 방식 disable
http
.httpBasic((auth) -> auth.disable());
//경로별 인가 작업
http
.authorizeHttpRequests((auth) -> auth
.requestMatchers("/login", "/", "/join").permitAll()
.requestMatchers("/test").hasRole("USER")
.requestMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated());
http
.addFilterBefore(new JWTFilter(jwtUtil), LoginFilter.class)
.addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil), UsernamePasswordAuthenticationFilter.class);
//세션 설정
http
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}