Security에 대하여

Security란 무엇인가

Spring Security는 스프링에서 제공하는 인증/인가 처리 전용 보안 프레임워크입니다.

주요 역할

  • 인증(Authentication): 이 사용자가 누구인가?
  • 인가(Authorization): 이 사용자가 이 기능을 사용할 권한이 있는가?

  • 로그인 처리 (세션 or 토큰)
  • URL, 메서드, 도메인 기반 권한 제어
  • 비밀번호 암호화 및 검증
  • 세션 관리, 로그아웃, CSRF 방어
  • OAuth2 (카카오, 구글 등 소셜 로그인)
  • 필터 체인 기반의 보안 처리

Security를 사용하는 이유

security없이 직접 보안을 구현 시

  • 사용자 로그인 후, JWT 직접 생성 또는 세션 설정
  • 각 API 마다 사용자 인증 체크
  • 권한(Role) 체크를 코드 내부에서 수행
  • 인증 실패, 권한 없음 등의 직접 예외 처리
if (!"ADMIN".equals(user.getRole())) {
    throw new UnauthorizedException();
}
문제점 설명
중복 코드 많음 모든 API마다 인증/인가 로직 반복
예외 처리 복잡 인증 실패, 권한 없음 처리 직접 구현
보안 취약 CSRF, 세션 고정 등 방어 어려움
확장성 어려움 소셜 로그인, 권한 계층 구조성 등 구현 복잡

Security 도입 시

기능 설명
인증/인가 분리 로직과 보안 코드 명확히 분리됨
보안 필터 자동 구성 요청 전 필터에서 인증/인가 처리
다양한 로그인 방식 지원 Form, OAuth2, JWT, 세션 등
세밀한 권한 제어 URL, 메서드 단위까지 제어 가능
테스트, 유지보수 용이 역할 기반 접근 가능, 단위 테스트도 유리

Spring Security 내부 동작 흐름

Request
  ↓
Security Filter Chain (인증/인가 처리)
  ↓
DispatcherServlet
  ↓
Controller (비즈니스 로직)

요청 -> Security Fliter Chain에서 인증/인가 관련 칠터들을 통과 -> 인증 성공 : SecurityContest에 사용자 정보 저장 -> DispatcherServlet을 거쳐 컨트롤러에 도달

구성 요소 역할
UsernamePasswordAuthenticationFilter 로그인 시 아이디/비밀번호 처리
SecurityContext 인증 정보 저장 공간
UserDetailsService 사용자 정보 로드 서비스
AuthenticationManager 인증 성공 여부 판단
AccessDecisionManager 권한 확인

실제 Security 설정 예시

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeHttpRequests()
                .requestMatchers("/admin/**").hasRole("ADMIN") // 관리자 전용 URL 설정
                .requestMatchers("/login", "/signup").permitAll() // 인증 없이 접근 허용
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/")
            .and()
            .logout()
                .logoutSuccessUrl("/login?logout");
        return http.build();
    }
}


Security 없이 구현했던 방식

1) 세션 기반 인증

세션: Jaca EE에서 기본 제공. 서버가 세션에 로그인 정보를 저장하고, 클라이언트는 쿠키를 통해 세션 ID를 전달함.

  1. 로그인 성공 시 HttpSession 객체 생성
  2. 세션 ID를 쿠키에 담아 브라우저에 저장
  3. 이후 요청마다 해당 세션 ID로 사용자 식별
@PostMapping("/login")
public String login(HttpServletRequest request, @RequestBody LoginDto dto) {
    User user = userRepository.findByUsername(dto.username());
    if (user.getPassword().equals(dto.password())) {
        request.getSession().setAttribute("loginUser", user.getId());
        return "로그인 성공";
    }
    return "로그인 실패";
}

단점

  • 서버가 Session을 유지해야 함
  • 클러스터 환경에서 세션 공유 필요
  • 세션 고정, 탈취 등 보안 이슈

2) JWT 기반 인증

토큰에 사용자 정보를 담아 클라이언트가 보유. 서버는 상태를 유지하지 않아 확장성이 뛰어남.

  1. 로그인 시 서버에서 JWT 토큰 생성 -> 클라이언트 전달
  2. 클라이언트는 이후 모든 요청에 토큰을 Authorization 헤더에 담아 전달
  3. 서버는 토큰을 검증 후, 사용자 정보 추출
String token = request.getHeader("Authorization").replace("Bearer ", "");
Claims claims = jwtService.parseToken(token);

장점

  • Stateless -> 서버 확장 용이
  • 토큰 하나로 인증 가능

단점

  • 토큰 만료 및 갱신 처리 복잡
  • 토큰 탈취 시 대응 어려움
  • 로그아웃 기능 직접 구현 필요

세션 vs JWT

항목 세션 기반 인증 JWT 기반 인증
인증 방식 서버가 세션에 사용자 정보를 저장 클라이언트가 토큰을 보유하고 인증
서버 상태 상태가 있음 (Stateful) 무상태 (Stateless)
저장 위치 서버 메모리, Redis 등 클라이언트(쿠키, localStorage 등)
확장성 서버 간 세션 공유 필요 → 확장 어려움 서버 간 상태 공유 불필요 → 확장 쉬움
보안 위험 세션 탈취, 세션 고정 공격 토큰 탈취, 재사용 공격
유효기간 관리 세션 만료 시간 설정 토큰에 만료 정보 포함 (exp)
복잡도 구현 간단, 유지보수 쉬움 구현 복잡하지만 유연함

정리

  • Spring Security는 복잡한 인증/인가 로직을 자동화하여 보안 수준을 높여줌
  • 직접 구현하는 방식은 유연하지만, 유지보수나 보안 리스크가 큼
  • 세션 기반은 단순하지만 상태 유지가 필요하고, JWT는 무상태로 확장성이 뛰어남
  • 서비스의 특성과 규모에 따라 인증 방식과 보안 전략을 적절히 선택하는 것이 중요!