Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Archives
Today
Total
관리 메뉴

밤빵's 개발일지

[TIL]20240803 스프링 시큐리티(Spring Security) 본문

개발Article

[TIL]20240803 스프링 시큐리티(Spring Security)

최밤빵 2024. 8. 3. 22:26

시큐리티... 강의를 들으면서도 어려웠고, 직접 구현하려니 더 어려웠던 건데, 지금 진행하는 프로젝트에서 이미 구현이 되어있어서 내가 작성하진 않지만 구현정도는 할 줄 알아야하지않을까?란 생각에 오늘 개발일지 소재로 정했다.

🤓스프링 시큐리티(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 토큰을 포함해야 한다.

→ 적절한 예외 처리:

인증과 인가 실패 시 사용자에게 적절한 예외 메시지를 제공하고, 에러 페이지를 구성하여 사용자 경험을 개선해야 한다.

→ 필터 순서 관리:

스프링 시큐리티 필터 체인은 요청 처리 순서에 따라 작동하므로, 커스텀 필터를 추가할 때는 필터 순서를 적절히 관리해야 한다.

→ 보안 강화:

보안 설정을 최소한으로 유지하지 말고, 필요에 따라 강력한 보안 설정을 추가해야 한다. 특히, 중요한 리소스나 민감한 데이터가 포함된 경우 접근 제어를 철저히 설정해야 한다.

 

▶ 정리 

스프링 시큐리티의 개념과 주요 기능, 동작 원리, 핵심 구성 요소, 사용 예시, 그리고 사용 시 주의사항에 대해 알아보면서 스프링 시큐리티는 애플리케이션의 보안을 강화하는 매우 강력한 도구이며, 유연하고 확장 가능한 아키텍처로 다양한 보안 요구사항을 처리할 수 있다는걸 조금은 이해할 수 있게 됐는데.. 이해한건 이해한거고, 구현한다고 생각하니 벌써 머리가 아픈 것 같다🤣