Spring_20_Spring Security

OngTK·2025년 10월 23일

Spring

목록 보기
20/25

🔐 Spring Security 정리 (JWT + 커스텀 필터 중심)


1) 스프링 시큐리티 개요

  • 정의: Spring 기반 애플리케이션에 인증(Authentication)인가(Authorization) 를 제공하는 라이브러리
  • 대표 기능: 로그인/로그아웃, 소셜 로그인, CSRF 방어, URL 접근 제어, 필터 체인 기반 보안

2) 의존성

implementation 'org.springframework.boot:spring-boot-starter-security'
  • 추가 즉시 기본 보안 필터가 활성화됨(모든 요청이 인증 필요 상태로 바뀜).

3) 보안 설정 — SecurityConfig

3-1. 전체 코드

@Configuration
@RequiredArgsConstructor
public class SecurityConfig {

    // [1] 개발자가 만든 JWT 인증 필터 (스프링 시큐리티 체인에 조립)
    private final JwtAuthFilter jwtAuthFilter;

    /**
     * [2] HTTP 보안 필터 체인 커스텀
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        // [2.1] URL 접근 권한(인가) 규칙
        http.authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/user/info").hasAnyRole("ADMIN", "USER") // 둘 중 하나면 허용
                .requestMatchers("/api/admin/**").hasRole("ADMIN")             // ADMIN 전용
                .requestMatchers("/**").permitAll()                            // 그 외 모두 허용(가장 하단)
        );

        // [2.2] CSRF 설정
        http.csrf(csrf -> csrf.disable());

        // [2.3] 세션 정책
        http.sessionManagement(session ->
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        // [2.4] 커스텀 JWT 필터를 UsernamePasswordAuthenticationFilter 이전에 삽입
        http.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        // [2.5] 위 설정을 바탕으로 SecurityFilterChain 빌드
        return http.build();
    }
}

3-2. 주요 API & 매개변수 설명

메서드설명
authorizeHttpRequests()URL별 접근 권한(인가) 설정
.requestMatchers("/api/admin/**")특정 URL 패턴 지정
.hasRole("ADMIN")ROLE_ADMIN 권한 보유자만 접근 허용
.hasAnyRole("ADMIN","USER")두 권한 중 하나라도 있으면 허용
.permitAll()인증 없이 접근 허용
csrf().disable()개발용: CSRF 방어 비활성화
sessionManagement().sessionCreationPolicy(STATELESS)JWT 기반: 세션 미사용
addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)커스텀 JWT 필터를 인증 필터보다 먼저 실행

4) 커스텀 JWT 필터 — JwtAuthFilter

@Component
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {

    // [1] 개발자가 만든 JWT 서비스 (토큰 생성/검증/추출)
    private final JwtService jwtService;

    /**
     * [2] 매 요청마다 단 한 번만 수행되는 필터.
     * - 쿠키에서 토큰을 꺼내고
     * - 유효하면 Spring Security 인증 토큰(UsernamePasswordAuthenticationToken)을 만들어
     * - SecurityContextHolder에 저장
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {

        // [2.1] 쿠키에서 토큰 추출
        String token = null;
        if (request.getCookies() != null) {
            for (Cookie cookie : request.getCookies()) {
                if ("loginUser".equals(cookie.getName())) {
                    token = cookie.getValue();
                    break;
                }
            }
        }

        // [2.2] 토큰 유효성 검사 후 SecurityContext에 인증 정보 저장
        if (token != null && jwtService.checkToken(token)) {
            String uid   = jwtService.getUid(token);
            String urole = jwtService.getUrole(token);

            // [a] 인증 객체 생성
            UsernamePasswordAuthenticationToken authToken =
                    new UsernamePasswordAuthenticationToken(
                            uid,                                   // principal: 사용자 식별자
                            null,                                  // credentials: 비밀번호는 이미 검증됨
                            List.of(new SimpleGrantedAuthority("ROLE_" + urole)) // 권한 부여
                    );

            // [b] SecurityContext에 인증 정보 저장
            SecurityContextHolder.getContext().setAuthentication(authToken);
        }

        // [3] 체인의 다음 필터로 요청 전달 (항상 호출해야 함)
        filterChain.doFilter(request, response);
    }
}

5) 주요 클래스 / 메서드 상세 설명

항목설명
OncePerRequestFilter요청마다 한 번만 실행되는 스프링 필터 베이스 클래스
doFilterInternal()요청/응답/필터 체인을 받아 직접 인증 로직을 구현
HttpServletRequest쿠키, 헤더 등 요청 데이터 접근
FilterChain다음 필터 호출 (doFilter() 호출 필수)
UsernamePasswordAuthenticationToken인증 객체로, principal(사용자), credentials(비밀번호), 권한 정보 포함
SecurityContextHolder현재 요청 스레드의 보안 컨텍스트 저장소. 인증 성공 시 Authentication을 저장
SimpleGrantedAuthority("ROLE_" + role)문자열 권한을 스프링 시큐리티 권한 객체로 변환
addFilterBefore()커스텀 필터를 기존 시큐리티 필터 체인에 삽입
SessionCreationPolicy.STATELESS세션 사용하지 않고 JWT로만 인증 상태 유지

6) 실무 팁

  • STATELESS 정책: 세션 저장소가 없으므로 서버 확장성(Scale-out)에 유리
  • CSRF 설정: 쿠키 기반 JWT 사용 시 HttpOnly + Secure + SameSite 설정 병행
  • 권한 규칙: 항상 대문자(ADMIN, USER) 사용
  • @PreAuthorize("hasRole('ADMIN')"): 메서드 단위 권한 검증 시 사용
  • 토큰 위치: Authorization 헤더(Bearer <JWT>) 방식도 가능

7) 빠른 레퍼런스

항목핵심 내용
URL 인가authorizeHttpRequests().requestMatchers().hasRole()/permitAll()
CSRF개발 시 비활성화, 운영 시 예외 지정
세션SessionCreationPolicy.STATELESS
필터 삽입addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
인증 주입SecurityContextHolder.getContext().setAuthentication(auth)
권한 타입SimpleGrantedAuthority("ROLE_ADMIN")

profile
2025.05.~K디지털_풀스택 수업 수강중

0개의 댓글