2024. 4. 5. 16:49ㆍProject/2023-02 캡스톤디자인
Spring Security는 Spring 기반의 애플리케이션에서 보안 분야에서 사용되는 인증 및 접근 제어를 담당한다. 주요 목적은 Java 애플리케이션에 대한 인증과 인가를 제공하는 것으로, 웹 보안과 메소드 수준 보안을 모두 지원한다.
Spring Security는 다음과 같은 역할을 수행한다.
- 인증(Authentication): 사용자가 누구인지 확인하는 프로세스이며, Spring Security는 폼 기반 인증, HTTP 기본 인증, LDAP, OAuth2 등 다양한 인증 메커니즘을 지원한다.
- 인가(Authorization): 인증된 사용자가 수행할 수 있는 작업을 결정한다. 예를 들어, 특정 역할을 가진 사용자만 특정 웹 URL에 접근하거나, 특정 메소드를 실행할 수 있는 권한을 제한할 수 있다.
- 보안 헤더: Spring Security는 보안과 관련된 다양한 HTTP 헤더를 설정하여, 보안 취약점을 줄일 수 있도록 도와준다.
- CSRF(Cross-Site Request Forgery) 보호: CSRF 공격을 방지하기 위한 메커니즘을 기본적으로 활성화하고 있으며, 요청에 대한 CSRF 토큰 검증을 수행한다.
- 세션 관리: 동시 세션 제어, 세션 고정 보호, 세션 만료 등 세션 수준에서의 보안을 관리한다.
- 메소드 수준 보안: 서비스 또는 컴포넌트 레벨에서 메소드 호출에 대한 보안을 적용할 수 있다. 이를 통해 세밀한 권한 제어가 가능하다.
CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조)란?
웹 보안 취약점 중 하나로, 공격자가 사용자의 브라우저를 이용하여 사용자가 의도하지 않은 행동을 웹 애플리케이션에 요청하게 만드는 공격 방법이다. 사용자가 웹 사이트에 로그인한 상태에서 CSRF 공격을 받게 되면, 공격자는 사용자의 인증 정보를 이용해 악의적인 변경사항을 요청할 수 있다.
Spring Security 동작 방식
Spring Security는 필터 기반의 보안을 사용한다. 클라이언트로부터 오는 요청은 여러 필터를 거쳐 가공되고 검증하게 된다. 이 필터 체인을 통해 인증 및 권한 부여 등의 보안 관련 처리가 실행된다.
각 필터는 클라이언트에게 request를 받으면 DispatcherServlet에 도달하기 전에 거치게 된다.
Spring Security Filter를 디테일하게 나눈다면 다음과 같다.
Filter와 Interceptor와의 차이점
- 필터와 인터셉터는 두 방식 모두 인증과 인가를 구현할 수 있지만 둘의 차이점은 작동 위치가 다르다는 점이다.
- 필터는 DispatcherServlet 도달하기 전에 작동하지만 인터셉터는 DispatcherServlet 도달 이후 Controller에 도달하기 전에 작동하게 된다.
- 필터는 어플리케이션의 전체 요청 처리 플로우에 대한 보안을 제공하는 반면, 인터셉터는 특정 요청이나 특정 단계에서 추가적인 처리를 제공하는 데 더 특화되어 있다.
일반적으로 웹 플로우 방어에서 필터를 사용하는 것이 더 효율적이기 때문에 필터를 사용하여 Spring Security에서 사용되는 필터들과 관련 클래스들을 구현해보려고 한다.
SecurityConfig 클래스 -> 필터 체인 정의
@Configuration
@EnableWebSecurity
@AllArgsConstructor
public class SecurityConfig extends AbstractHttpConfigurer {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("X-Requested-With", "Content-Type", "Authorization", "X-XSRF-token"));
configuration.setAllowCredentials(false);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, CustomAuthenticationFilter customAuthenticationFilter,
JwtAuthorizationFilter jwtAuthorizationFilter) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
// request 헤더에서 JWT 토큰을 추출하고 해당 토큰의 유효성을 검증하는 역할 수행
.addFilterBefore(jwtAuthorizationFilter, BasicAuthenticationFilter.class)
// STATELESS -> 스프링 시큐리티가 세션을 사용하지 않도록 설정.
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeRequests(authz -> authz
.requestMatchers("/api/users/login","/api/users/register").permitAll() // 공개 API
.anyRequest().authenticated()
)
.addFilterBefore(customAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
@Bean
public CustomAuthenticationFilter customAuthenticationFilter(
AuthenticationManager authenticationManager,
CustomAuthSuccessHandler customAuthSuccessHandler,
CustomAuthFailureHandler customAuthFailureHandler) {
CustomAuthenticationFilter customAuthenticationFilter = new CustomAuthenticationFilter(authenticationManager);
customAuthenticationFilter.setFilterProcessesUrl("/api/users/login");
customAuthenticationFilter.setAuthenticationSuccessHandler(customAuthSuccessHandler);
customAuthenticationFilter.setAuthenticationFailureHandler(customAuthFailureHandler);
customAuthenticationFilter.afterPropertiesSet();
return customAuthenticationFilter;
}
@Bean
public AuthenticationManager authenticationManager(CustomAuthenticationProvider customAuthenticationProvider) {
return new ProviderManager(Collections.singletonList(customAuthenticationProvider));
}
@Bean
public CustomAuthenticationProvider customAuthenticationProvider(CustomUserDetailsService userDetailsService) {
return new CustomAuthenticationProvider(userDetailsService);
}
@Bean
public CustomAuthSuccessHandler customLoginSuccessHandler(ObjectMapper objectMapper, TokenService tokenService) {
return new CustomAuthSuccessHandler(objectMapper, tokenService);
}
@Bean
public CustomAuthFailureHandler customLoginFailureHandler() {
return new CustomAuthFailureHandler();
}
@Bean
public JwtAuthorizationFilter jwtAuthorizationFilter(
CustomUserDetailsService customUserDetailsService,
TokenService tokenService,
ObjectMapper objectMapper) {
return new JwtAuthorizationFilter(customUserDetailsService, tokenService, objectMapper);
}
private AuthorizationDecision isAdmin(
Supplier<Authentication> authenticationSupplier,
RequestAuthorizationContext requestAuthorizationContext
) {
return new AuthorizationDecision(
authenticationSupplier.get()
.getAuthorities()
.contains(new SimpleGrantedAuthority("ADMIN"))
);
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
- corsConfigurationSource: CORS(Cross-Origin Resource Sharing) 설정을 정의한다. 여기서는 모든 origin에서의 요청을 허용하고, 특정 HTTP 메소드와 헤더를 허용하는 설정을 포함한다.
- filterChain: Spring Security의 필터 체인을 구성한다. 여기서는 수행하는 역할은 CSRF 보호를 비활성화하고, JWT 인증 필터와 사용자 정의 인증 필터를 필터 체인에 추가한다. 세션 관리 전략을 설정하고, 특정 경로에 대한 접근 권한을 설정한다. 이를 통해 인증(Authentication)과 인가(Authorization) 작업을 수행할 수 있게 된다.
- customAuthenticationFilter: 인증(Authentication)을 위한 필터로서, 사용자 정의 인증 필터를 생성한다. 해당 필터는 로그인 경로에서 사용자의 인증을 처리하고, 인증 성공 또는 실패 시에 특정 핸들러를 호출하게 된다.
- authenticationManager: 인증 프로세스를 관리하는 AuthenticationManager 빈을 생성한다. 이는 CustomAuthenticationProvider를 사용하여 인증을 수행하게 된다.
- customAuthenticationProvider: 사용자 정의 AuthenticationProvider를 생성한다. 해당 클래서는 CustomUserDetailsService를 사용하여 사용자 정보를 조회하고, 해당 정보를 기반으로 인증을 수행한다.
- customLoginSuccessHandler 및 customLoginFailureHandler: 인증 성공 및 실패 시 호출될 핸들러를 정의하는 클래스이다. 각 핸들러는 인증 결과에 따라 적절한 처리를 수행한다.
- jwtAuthorizationFilter: 인가(Authorization)를 위한 필터로서, JWT 토큰을 검증하는 필터를 생성한다. 해당 필터는 헤더에서 JWT 토큰을 추출하고, 토큰의 유효성을 검증한 후, 유효한 경우 사용자 정보를 SecurityContext에 설정한다.
- passwordEncoder: 비밀번호를 암호화하기 위한 BCryptPasswordEncoder 빈을 생성한다.
Reference
https://velog.io/@zini9188/Spring-Security-Filter%EC%99%80-FilterChain
'Project > 2023-02 캡스톤디자인' 카테고리의 다른 글
[Spring Security] SpringBoot 3.1.x Redis 설정 (with JWT) (0) | 2024.04.08 |
---|---|
[Spring Security] Filter 구현 (2) (0) | 2024.04.05 |
[Flask] ResNet활용 API 개발 (1) | 2024.03.07 |
Haversine 공식을 이용한 거리순 반환 로직 개발 (0) | 2024.03.07 |
🩹 Image Classification을 활용한 피부질환 진단 서비스 개발 (0) | 2024.03.06 |