밤빵's 개발일지
[TIL]20240803 스프링 시큐리티(Spring Security) 본문
시큐리티... 강의를 들으면서도 어려웠고, 직접 구현하려니 더 어려웠던 건데, 지금 진행하는 프로젝트에서 이미 구현이 되어있어서 내가 작성하진 않지만 구현정도는 할 줄 알아야하지않을까?란 생각에 오늘 개발일지 소재로 정했다.
🤓스프링 시큐리티(Spring Security)란?
스프링 시큐리티(Spring Security)는 스프링 애플리케이션에서 보안을 제공하기 위한 서블릿 필터 기반의 보안 프레임워크이다. 스프링 시큐리티는 웹 애플리케이션의 인증과 인가를 처리하며, 개발자가 쉽게 보안 로직을 구현하고 확장할 수 있는 다양한 기능을 제공한다. 스프링 부트(Spring Boot)와 함께 사용하면 보안 구성을 더 간단하게 자동화할 수 있다.
▶ 스프링 시큐리티의 주요 개념
스프링 시큐리티의 주요 개념은 다음과 같다:
→ 인증(Authentication):
인증은 사용자가 누구인지 확인하는 과정이다. 일반적으로 사용자 이름과 비밀번호 또는 토큰을 사용하여 사용자를 인증한다. 스프링 시큐리티는 다양한 인증 메커니즘을 지원하며, 커스텀 인증 로직도 쉽게 추가할 수 있다.
→ 인가(Authorization):
인가는 인증된 사용자가 특정 리소스나 기능에 접근할 수 있는 권한이 있는지 확인하는 과정이다. 스프링 시큐리티는 역할(Role) 기반 접근 제어(RBAC)와 같은 다양한 인가 방식을 지원한다.
→ 서블릿 필터(Servlet Filter) 기반 보안:
스프링 시큐리티는 서블릿 필터를 사용하여 요청과 응답을 가로채고 보안 처리를 수행한다. 필터 체인(Filter Chain)을 통해 보안 작업을 처리하며, 각 필터는 특정 보안 로직을 수행한다.
→ 보안 컨텍스트(Security Context):
보안 컨텍스트는 인증된 사용자의 세부 정보를 유지하는 컨테이너로, SecurityContext에 저장된다. 애플리케이션의 어디에서나 보안 컨텍스트를 사용하여 현재 사용자의 인증 정보에 접근할 수 있다.
▶ 스프링 시큐리티의 주요 기능
스프링 시큐리티는 다음과 같은 주요 기능을 제공한다:
→ 세션 관리:
사용자의 세션을 관리하고, 세션 고정 공격(Session Fixation Attack)을 방지할 수 있는 기능을 제공한다. 세션을 공유하지 않도록 보호하고, 세션 만료나 중복 로그인 방지 기능도 지원한다.
→ CSRF(Cross-Site Request Forgery) 보호:
CSRF는 사용자가 의도하지 않은 요청을 수행하도록 유도하는 공격이다. 스프링 시큐리티는 기본적으로 CSRF 방어 기능을 제공하여 웹 애플리케이션의 보안을 강화한다.
→ 암호화된 비밀번호 관리:
사용자 비밀번호를 안전하게 저장하기 위해 암호화 기능을 제공하며, BCryptPasswordEncoder와 같은 다양한 암호화 알고리즘을 쉽게 사용할 수 있다.
→ 보안 필터 체인(Security Filter Chain):
스프링 시큐리티는 요청과 응답을 가로채는 여러 개의 보안 필터로 구성된 필터 체인을 제공한다. 각 필터는 인증, 인가, 세션 관리 등 특정 보안 작업을 수행하며, 사용자 정의 필터를 추가하거나 순서를 조정할 수 있다.
→ OAuth2 및 JWT 지원:
OAuth2와 JWT(Json Web Token)를 사용한 인증 및 인가를 손쉽게 통합할 수 있으며, 다양한 소셜 로그인 및 SSO(Single Sign-On) 기능을 제공한다.
→ 메서드 수준 보안:
메서드 단위로 접근 제어를 설정할 수 있으며, @PreAuthorize, @Secured와 같은 애너테이션을 사용하여 서비스 계층에서 세부적인 인가 규칙을 정의할 수 있다.
▶ 스프링 시큐리티의 동작 원리
스프링 시큐리티는 서블릿 필터 체인을 통해 웹 애플리케이션의 보안을 처리한다. 스프링 시큐리티의 동작 과정은 다음과 같다:
→ 요청이 애플리케이션에 도달:
클라이언트의 요청이 서버에 도달하면, 스프링 시큐리티의 필터 체인(Filter Chain)에 의해 요청이 가로채진다.
→ 인증 및 인가 필터 처리:
필터 체인은 각 필터를 순차적으로 실행하며, 인증 및 인가와 관련된 로직을 처리한다. 기본적으로 UsernamePasswordAuthenticationFilter, BasicAuthenticationFilter, OAuth2LoginAuthenticationFilter 등의 필터가 사용된다.
→ 인증 매니저(Authentication Manager)와 인증 프로바이더(Authentication Provider):
AuthenticationManager는 인증 요청을 처리하고, AuthenticationProvider는 실제 인증 로직을 담당한다. 인증이 성공하면 SecurityContextHolder에 사용자 정보를 저장하고, 실패하면 AuthenticationEntryPoint를 통해 예외를 처리한다.
→ 보안 컨텍스트 설정:
인증이 성공하면, 사용자 정보가 SecurityContext에 설정되어 이후 요청에서 접근할 수 있도록 한다. 이를 통해 현재 사용자 정보를 전역적으로 사용할 수 있다.
→ 인가 처리:
인증이 완료된 후, 사용자가 요청한 리소스에 접근할 수 있는 권한이 있는지 검사한다. 권한이 없으면 AccessDeniedHandler를 통해 접근 거부 처리를 수행한다.
▶ 스프링 시큐리티의 핵심 구성 요소
스프링 시큐리티는 다양한 구성 요소로 이루어져 있으며, 각 요소는 보안 처리를 위한 핵심 역할을 한다:
→ SecurityContext와 SecurityContextHolder:
SecurityContext는 현재 인증된 사용자의 보안 정보를 담고 있으며, SecurityContextHolder는 애플리케이션의 어디서나 SecurityContext에 접근할 수 있는 전역 객체이다.
→ Authentication 객체:
Authentication 객체는 사용자의 인증 정보를 나타낸다. 이 객체는 Principal(주체), Credentials(자격 증명), Authorities(권한)로 구성된다.
→ UserDetails와 UserDetailsService:
UserDetails는 사용자 정보를 캡슐화하는 인터페이스로, 사용자 이름, 비밀번호, 권한 정보 등을 포함한다. UserDetailsService는 사용자 정보를 제공하는 서비스로, 스프링 시큐리티의 인증 프로세스에서 사용된다.
→ GrantedAuthority:
GrantedAuthority는 사용자가 가지고 있는 권한을 나타내는 인터페이스이다. 각 사용자는 여러 개의 권한을 가질 수 있으며, 이는 ROLE_USER, ROLE_ADMIN과 같은 형태로 정의된다.
→ PasswordEncoder:
PasswordEncoder는 사용자 비밀번호를 암호화하고, 이를 검증하는 역할을 한다. BCryptPasswordEncoder, Pbkdf2PasswordEncoder 등 다양한 구현체를 제공하여 안전한 비밀번호 저장을 지원한다.
▶ 스프링 시큐리티 설정 예시
다음은 스프링 시큐리티를 사용하여 JWT 토큰을 검증하는 AdditionalSecurityConfig 클래스와 기본 시큐리티 설정을 하는 SecurityConfig 클래스의 예시이다.
▽ 예시 1: AdditionalSecurityConfig
package com.example.dischord.global.config;
import com.example.dischord.global.filter.JwtFilter;
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.core.userdetails.UserDetailsService;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class AdditionalSecurityConfig {
private final JwtUtil jwtUtil;
private final UserDetailsService userDetailsService;
public AdditionalSecurityConfig(JwtUtil jwtUtil, UserDetailsService userDetailsService) {
this.jwtUtil = jwtUtil;
this.userDetailsService = userDetailsService;
}
@Bean
public SecurityFilterChain additionalFilterChain(HttpSecurity http) throws Exception {
JwtFilter jwtFilter = new JwtFilter(jwtUtil, userDetailsService);
// CSRF 비활성화
http.csrf(csrf -> csrf.disable())
.formLogin(form -> form.disable()) // 폼 로그인 비활성화
.httpBasic(httpBasic -> httpBasic.disable()) // HTTP 기본 인증 비활성화
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll()) // 모든 요청 허용
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 세션 상태 설정
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); // JWT 필터 추가
return http.build();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}
→ @EnableWebSecurity 애너테이션으로 스프링 시큐리티를 활성화했다.
additionalFilterChain 메서드에서는 JwtFilter를 추가하고, CSRF, 폼 로그인, HTTP 기본 인증을 비활성화했다.
SessionCreationPolicy.STATELESS를 사용하여 세션을 사용하지 않고, JWT 토큰을 통한 인증을 처리하도록 했다.
▽ 예시 2: SecurityConfig
package com.example.dischord.global.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); // 비밀번호 암호화를 위한 BCryptPasswordEncoder 빈 등록
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf((auth) -> auth.disable()) // CSRF 보호 비활성화 (JWT를 사용하는 경우 필요 없음)
.formLogin((auth) -> auth.disable()) // 폼 로그인 비활성화
.httpBasic((auth) -> auth.disable()) // HTTP 기본 인증 비활성화
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll()) // 모든 요청 허용
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // 세션을 사용하지 않는 설정
return http.build();
}
}
→ passwordEncoder 메서드는 BCryptPasswordEncoder를 빈으로 등록하여 비밀번호 암호화를 제공한다.filterChain 메서드에서는 CSRF 보호, 폼 로그인, HTTP 기본 인증을 비활성화했다. 모든 요청을 허용하고, 세션을 사용하지 않도록 SessionCreationPolicy.STATELESS를 설정했다.
▶ 스프링 시큐리티 사용 시 주의사항
→ 비밀번호 암호화:
비밀번호는 절대로 평문으로 저장해서는 안 된다. 항상 PasswordEncoder를 사용하여 안전하게 암호화하고 저장해야 한다.
→ CSRF 보호:
스프링 시큐리티는 기본적으로 CSRF 보호 기능을 활성화한다. 이를 비활성화하지 않는 한, 모든 POST 요청은 CSRF 토큰을 포함해야 한다.
→ 적절한 예외 처리:
인증과 인가 실패 시 사용자에게 적절한 예외 메시지를 제공하고, 에러 페이지를 구성하여 사용자 경험을 개선해야 한다.
→ 필터 순서 관리:
스프링 시큐리티 필터 체인은 요청 처리 순서에 따라 작동하므로, 커스텀 필터를 추가할 때는 필터 순서를 적절히 관리해야 한다.
→ 보안 강화:
보안 설정을 최소한으로 유지하지 말고, 필요에 따라 강력한 보안 설정을 추가해야 한다. 특히, 중요한 리소스나 민감한 데이터가 포함된 경우 접근 제어를 철저히 설정해야 한다.
▶ 정리
스프링 시큐리티의 개념과 주요 기능, 동작 원리, 핵심 구성 요소, 사용 예시, 그리고 사용 시 주의사항에 대해 알아보면서 스프링 시큐리티는 애플리케이션의 보안을 강화하는 매우 강력한 도구이며, 유연하고 확장 가능한 아키텍처로 다양한 보안 요구사항을 처리할 수 있다는걸 조금은 이해할 수 있게 됐는데.. 이해한건 이해한거고, 구현한다고 생각하니 벌써 머리가 아픈 것 같다🤣
'개발Article' 카테고리의 다른 글
[TIL]20240805 웹소켓(WebSocket) (0) | 2024.08.05 |
---|---|
[WIL]20240804 멘토링CRUD에 아티클기능을 추가해주세요..? (1) | 2024.08.04 |
[TIL]20240802 Filter (0) | 2024.08.02 |
[TIL]20240801 로드밸런서 (0) | 2024.08.02 |
[TIL]20240731 쿠키와 세션 (0) | 2024.08.02 |