[백엔드 기초] Filter, Spring Security

2025. 10. 24. 12:26·Spring/백엔드 기초

어제 인증 인가 개념을 통한 회원가입, 로그인 화면 구성과 접근방식을 알아보았다. 

 

학습했던 내용에 추가적으로 알아볼 Filter, Security 내용에 대해 정리하고자 한다.

 

Filter란? 

: Filter란 Web 애플리케이션에서 관리되는 영역으로 Client로부터 오는 요청과 응답에 대해 최초/최종 단계의 위치이며 이를 통해 요청과 응답의 정보를 변경하거나 부가적인 기능을 추가할 수 있음

  • 주로 로깅 및 보안 처리에 활용되며 인증, 인가와 관련된 로직들을 처리할 수 있음 
    • 인증, 인가와 관련된 로직을 비즈니스 로직과 분리하여 관리할 수 있다는 장점이 있음.

- Filter Chain

  • Filter는 한 개만 존재하는 것이 아니라 여러개가 Chain 형식으로 묶여서 처리될 수 있음 
  • 아래의 실습 과정으로 경험했던 예시 코드를 통해 어떤식으로 구성되어 있는지 알아보자

1. Request URL Logging

@Slf4j(topic = "LoggingFilter")
@Component
@Order(1)
public class LoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 전처리
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String url = httpServletRequest.getRequestURI();
        log.info(url);

        chain.doFilter(request, response); // 다음 Filter 로 이동

        // 후처리
        log.info("비즈니스 로직 완료");
    }
}
  • @Order(1) 로 필터의 순서를 지정
  • chain.doFilter(request, response); 다음 필터로 이동시킴
  • log.info("비즈니스 로직 완료");
    • 작업이 다 완료된 후 Client에 응답 로그가 작성된 것을 확인할 수 있음

 

2. AuthFilter : 인증 및 인가 처리 필터

@Slf4j(topic = "AuthFilter")
@Component
@Order(2)
public class AuthFilter implements Filter {

    private final UserRepository userRepository;
    private final JwtUtil jwtUtil;

    public AuthFilter(UserRepository userRepository, JwtUtil jwtUtil) {
        this.userRepository = userRepository;
        this.jwtUtil = jwtUtil;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String url = httpServletRequest.getRequestURI();

        if (StringUtils.hasText(url) &&
                (url.startsWith("/api/user") || url.startsWith("/css") || url.startsWith("/js"))
        ) {
            // 회원가입, 로그인 관련 API 는 인증 필요없이 요청 진행
            chain.doFilter(request, response); // 다음 Filter 로 이동
        } else {
            // 나머지 API 요청은 인증 처리 진행
            // 토큰 확인
            String tokenValue = jwtUtil.getTokenFromRequest(httpServletRequest);

            if (StringUtils.hasText(tokenValue)) { // 토큰이 존재하면 검증 시작
                // JWT 토큰 substring
                String token = jwtUtil.substringToken(tokenValue);

                // 토큰 검증
                if (!jwtUtil.validateToken(token)) {
                    throw new IllegalArgumentException("Token Error");
                }

                // 토큰에서 사용자 정보 가져오기
                Claims info = jwtUtil.getUserInfoFromToken(token);

                User user = userRepository.findByUsername(info.getSubject()).orElseThrow(() ->
                        new NullPointerException("Not Found User")
                );

                request.setAttribute("user", user);
                chain.doFilter(request, response); // 다음 Filter 로 이동
            } else {
                throw new IllegalArgumentException("Not Found Token");
            }
        }
    }

}
  • httpServletRequest.getRequestURI() : 요청 URL을 가져와서 구분함(인가)
    • "/api/user", "/css", "/js"로 시작하는 URL은 인증처리에서 제외시킴
    • 그 외의 URL은 인증 처리를 진행 
      • jwtUtil.getTokenFromRequest(httpServletRequest);
        • httpServletRequest에서 Cookie 목록을 가져와 JWT가 저장된 Cookie를 찾음
        • getTokenFromRequest 메서드를 JwtUtil에 구현 
// HttpServletRequest 에서 Cookie Value : JWT 가져오기
public String getTokenFromRequest(HttpServletRequest req) {
    Cookie[] cookies = req.getCookies();
    if(cookies != null) {
        for (Cookie cookie : cookies) {
            if (cookie.getName().equals(AUTHORIZATION_HEADER)) {
                try {
                    return URLDecoder.decode(cookie.getValue(), "UTF-8"); // Encode 되어 넘어간 Value 다시 Decode
                } catch (UnsupportedEncodingException e) {
                    return null;
                }
            }
        }
    }
    return null;
}
  • tokenValue가 존재하면 토큰 파싱, 검증을 진행하고 사용자 정보를 가져옴
  • 가져온 사용자 username을 사용해서 DB에 사용자가 존재하는지 확인하고 존재하면 인증이 완료된 것
  • 사용자 정보가 필요한 Controller API에 인증완료된 User 객체를 전달해 줌
@Controller
@RequestMapping("/api")
public class ProductController {

    @GetMapping("/products")
    public String getProducts(HttpServletRequest req) {
        System.out.println("ProductController.getProducts : 인증 완료");
        User user = (User) req.getAttribute("user");
        System.out.println("user.getUsername() = " + user.getUsername());

       return "redirect:/";
    }
}

 

Spring Security란?

Spring securitysms Spring 서버에서 인증(로그인)과 인가(권한 부여)를 쉽게 구현할 수 있도록 다양한 기능을 제공하는 프레임워크

Spring Framework 가 웹 서버 구현을 쉽게 해주는 것처럼, spring security는 보안 관련 기능을 쉽게 구현할 수 있도록 도움

 

Spring Security는 필터 기반으로 동작하므로 위에서 진행했던 필터의 @Component 부분을 주석처리하고, 기능을 활성화 하도록 구성

package com.sparta.springauth.config;

import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity // Spring Security 지원을 가능하게 함
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // CSRF 설정
        http.csrf((csrf) -> csrf.disable());

        http.authorizeHttpRequests((authorizeHttpRequests) ->
                authorizeHttpRequests
                        .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() // resources 접근 허용 설정
                        .anyRequest().authenticated() // 그 외 모든 요청 인증처리
        );

        // 로그인 사용
        http.formLogin(Customizer.withDefaults());

        return http.build();
    }
}
  • 🔥CSRF란?
    • CSRF(사이트 간 요청 위조, Cross-site request forgery)
      • 공격자가 인증된 브라우저에 저장된 쿠키 세션 정보를 활용하여 웹 서버에 사용자가 의도하지 않은 요청을 전달
      • CSRF 설정이 되어있는 경우 html에서 CSRF 토큰 값을 넘겨주어야 요청을 수신 가능
      • 쿠키 기반의 취약점을 이용한 공격이기 때문에 REST 방식의 API에서는 비활성화가 가능하므로 비활성화 하고 진행

Spring Security 이해하기

: Spring에서 모든 호출은 DispatcherServlet을 통과하게 되고 이후에 각 요청을 담당하는 Controller로 분배됨

이 때, 요청에 대해 공통적으로 처리할 필요가 있을 때 DispatcherServlet 이전 단계가 필요하며 이것이 Filter라고 하는 것

  • Spring Security도 인증 및 인가를 처리하기 위해 Filter를 사용하는데 
    • Spring Security는 FilterChainProxy를 통해 상세로직을 구현하고 있음

Form Login 기반 인증

: Form Login 기반 인증은 인증이 필요한 URL 요청이 들어왔을 때 인증이 되지 않았다면 로그인 페이지를 반환하는 형태

 

UsernamePasswordAuthenticationFilter

: Form Login 인증에서 핵심 역할을 하는 필터

  • 인증 과정
    • 사용자가 사용자 이름과 비밀번호를 제출
    • 사용자의 정보가 담기는 토큰 객체를 생성
    • AuthenticationManager에게 인증을 시도
    • 실패하면 SecurityContextHolder를 비움
    • 성공하면 SecurityContextHolder에 Authentication을 세팅

 

SecurityContextHolder

  • SecurityContext는 인증이 완료된 사용자의 상세 정보(Authentication)를 저장
  • SecurityContext는 홀더로 접근할 수 있음 
// 예시코드
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = new UsernamePasswordAuthenticationToken(principal, credentials, authorities);
context.setAuthentication(authentication); // SecurityContext 에 인증 객체 Authentication 를 저장합니다.

SecurityContextHolder.setContext(context);

 

Authentication

 

  • 현재 인증된 사용자를 나타내며 SecurityContext에서 가져올 수 있음
    • principal : 사용자를 식별
    • credentials : 주로 비밀번호, 대부분 사용자 인증에서 사용한 후 비움
    • authorities : 사용자에게 부여한 권한을 GrantedAuthority로 추상화하여 사용 

 

이 내용을 바탕으로 Spring Security 사용 전과 후를 비교해보자면 아래와 같다

  • Spring Security 사용 전

  • Spring Security 사용 후

'Spring > 백엔드 기초' 카테고리의 다른 글

[백엔드 기초] Validation 개념 정리  (0) 2025.10.30
[백엔드 기초] RestTemplate  (0) 2025.10.24
[백엔드 기초] 쿠키를 활용한 인증 인가 개념 실습  (0) 2025.10.23
[백엔드 기초] 백엔드 기초 개념정리  (0) 2025.10.22
[백엔드 기초] 기초 개념 정리하기  (0) 2025.10.21
'Spring/백엔드 기초' 카테고리의 다른 글
  • [백엔드 기초] Validation 개념 정리
  • [백엔드 기초] RestTemplate
  • [백엔드 기초] 쿠키를 활용한 인증 인가 개념 실습
  • [백엔드 기초] 백엔드 기초 개념정리
stark77
stark77
하마의 IT 자기개발 이모저모, 백엔드 개발자로 거듭나기
  • stark77
    하마의 개발자 성장일기
    stark77
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 컴퓨터구조와 운영체제
        • 컴퓨터구조
        • 운영체제
      • SQL 기초
      • Spring
        • 백엔드 기초
        • Spring 실습
      • JAVA
        • Java 실습
      • HTML&CSS
        • HTML&CSS 실습
      • Git&GitHub
        • Git&GitHub 실습
      • 내배캠 끄적끄적
        • Today I Learned
      • 유용한 툴 및 사이트 정리
      • 취미
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    thymleaf
    java
    실시간 데이터 처리
    Til
    프로세스와 쓰레드
    Java 문법기초
    객체지향
    백엔드 기초다지기
    다형성
    Stomp
    git
    WebSocket
    Spriingboot
    MVC
    Github
    SpringSecurity
    객체지향프로그래밍
    BEAN
    algorithm
    HTML&CSS
    웹소켓
    경합조건과 교착상태
    백엔드 기초
    for문
    RestTemplate
    String.format
    네트워크 기초
    Spring
    jsp
    JPA
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
stark77
[백엔드 기초] Filter, Spring Security
상단으로

티스토리툴바