Spring Boot 개념 정리(인증/인가, Security) 과제 시작(ArgumentResolver)

제이 용·2025년 12월 3일

앞에 필터와 JWT를 사용하여 인증/인가를 구현해보았고 기본적인 개념들을 정리해보도록 하자.

또한 오늘 과제를 하면서 알게된 점을 정리해봅시다.


New Impo

@EntityGraph

  • @EntityGraph(attributePaths = "user")

    • 기존에 사용했던 방식은 user에 {}가 없었지만, 강의에는 있는 것을 발견했다.
    • {} 가 없어도 실행없는데는 지장이 없지만 여러 엔티티를 묶어야된다면, {"user, post"} 와 같이 사용할 수 있다는 것을 알게 되었다.

yml 파일의 환경변수

  • yml파일을 통해 오류를 해결하였고 커밋을 하는데 아차 싶었다.

    • jwt의 시크릿 키라던가, DB의 비밀번호 등 많은 정보가 있는데 커밋을 해버렸다.

    • 이걸 해결할 수 있는 방법이 .env 파일을 활용하는 것이었다.

  • 순서

1. .env파일 프로젝트에 생성
2. gitIgnore에 추가

application-local.yml
.env

3. .env파일에 진짜 키값들을 작성

DB_URL=jdbc:mysql://url~
DB_USERNAME=이름이름
DB_PASSWORD=비밀번호
DB_DRIVER=com.mysql.**
JWT_SECRET_KEY=키값

4. 실제 yml파일에는 불러올 수 있도록 설정

spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    driver-class-name: ${DB_DRIVER}
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: create-drop
    properties:
      hibernate:
        format_sql: true
    defer-datasource-initialization: true

jwt:
  secret:
    key: ${JWT_SECRET_KEY}
  • 다만 스프링에서 .env에 있는 값을 바로 처리를 하지않아 필요한 플러그인을 추가해야된다거나 빌드에 추가해야된다는 등 불편한 과정이 있기는 하지만, 커밋할 시 yml파일에 기밀 정보가 담기지 않기 때문에 마음 놓고 커밋을 해도 괜찮다.

    • 다만 .env 파일과 gitIgnore 에 파일은 절대로 커밋해서는 아니된다.

HandlerMethodArgumentResolver

ArgumentResolver

  • 스프링 MVC에서 컨트롤러 메서드의 파라미터를 해석해서 객체로 만들어 주는 확장 메서드이다.
  • 사용 타이밍
    • 컨트롤러 파라미터 바인딩을 커스텀하고 싶을 때
    • 로그인 사용자 정보, IP, 토큰 값 등 자동 매핑하고 싶을 때
  • ArgumentResolver가 요청(HttpServletRequest)에서 값을 꺼내 AuthUser 객체를 생성해준다.
@PostMapping("/todos")
public ResponseEntity<?> save(@Auth AuthUser authUser, @RequestBody TodoRequest request)

사용법

  • Resolver 클래스 구현

    • 어떤 파라미터를 지원하는지 정의 (supportsParameter)

    • 파라미터 객체를 어떻게 만들어 반환할지 정의 (resolveArgument)

  • Spring MVC에 등록

    • 스프링이 ArgumentResolver를 사용하려면 반드시 WebMvcConfigurer를 통해 명시적으로 등록해야한다.
  @Configuration
    public class WebConfig implements WebMvcConfigurer {

    private final AuthUserArgumentResolver authUserArgumentResolver;

    public WebConfig(AuthUserArgumentResolver authUserArgumentResolver) {
        this.authUserArgumentResolver = authUserArgumentResolver;
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(authUserArgumentResolver);
    }
   }

@Component 만 달면 동작하지 않는 이유

  • 빈(bean)으로 등록은 되지만 ArgumentResolver는 빈으로 등록만 한다고 스프링 MVC가 자동으로 사용하지 않기 떄문이다. 따라서 Resolver를 호출되지 않아 NullPointerException이 생길 수 있다.

스프링 MVC의 파라미터 처리 동작 순서

  • 스프링이 가진 기본 리졸버들 먼저 등록됨

  • 이후 WebMvcConfigurer → addArgumentResolvers() 목록을 확인

  • 여기에 추가된 리졸버만 실제로 동작

@Configuration

  • "이 클래스는 스프링 설정을 정의하는 클래스야."

  • WebMvcConfigurer의 내용을 스프링 MVC 구조에 실제로 적용하려면 스프링이 이 클래스를 설정으로 인식해야 하는데 그 역할을 @Configuration이 해준다.


개념정리

인증 / 인가

  • 인증 : “너 누구야?” 를 확인하는 과정

    • 사용자의 신원(Identity)을 확인하는 절차
    • 클라이언트가 자신이 누구인지를 증명
    • 아이디/비밀번호, 토큰, 인증서 등 다양한 방식 사용
  • 인가 : “너 이거 해도 돼?” 를 판단하는 과정

    • 인증된 사용자가 요청한 리소스나 기능에 접근할 권한이 있는지 판단하는 절차
    • 역할(Role), 권한(Authority)에 따라 접근 여부 결정

    인증에 성공하면, 사용자 정보(Principal)가 보안 컨텍스트(SecurityContext)에 저장됨


Spring Security 에서의 인증 인가

  • 인증: 사용자의 신원 확인
  • 인가: 권한 확인
  • 인증 없이 인가 X (항상 인증 → 인가 순서)
  • 스프링 시큐리티는 이 두 과정을 자동화 해주는 프레임워크

Spring Security

  • 스프링 기반 애플리케이션의 인증(Authentication)인가(Authorization)를 담당하는 보안 프레임워크

  • 특징

    • Filter 기반 보안 처리
    • 인증/인가 로직을 개발자가 직접 구현하지 않아도 되는 구조 제공
    • 다양한 인증 방식을 플러그인 형태로 확장 가능
  • 적용 요구사항

    • build.gradle에 추가
    implementation 'org.springframework.boot:spring-boot-starter-security'
  • 변화 내용

  • 기존에 있던 필터체인에 시큐리티 필터가 끼워들어가진다.

JWT 인증 방식 도입 시 변경 사항

Spring Security는 세션 방식이 기본이지만, JWT기반 인증을 사용하면 로그인 이후 모든 요청을 커스텀 필터에서 처리하게 된다.

[사용자 요청][JwtFilter (커스텀 필터)][Security Filter Chain][User 저장 및 인증 처리]

Security에 JwtFilter 등록법

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
            .csrf(AbstractHttpConfigurer::disable)  // .csrf().disable() 방식은 더 이상 사용 안함.
            .httpBasic(AbstractHttpConfigurer::disable) // BasicAuthenticationFilter 비활성화
            .formLogin(AbstractHttpConfigurer::disable) // UsernamePasswordAuthenticationFilter, DefaultLoginPageGeneratingFilter 비활성화
            .addFilterBefore(jwtFilter, SecurityContextHolderAwareRequestFilter.class)
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/login").permitAll()
                .anyRequest().authenticated()
            )
            .build();
    }
  • crsf : CSRF(Cross-Site Request Forgery) 보호를 비활성화

    • Why :: JWT 기반 인증에서는 crsf가 필요하지 않으니 비활성화
  • httpBasic : HTTP Basic 인증 방식을 비활성화

    • Why :: Authorization 헤더에 사용자 이름과 비밀번호를 평문으로 포함하는 방식으로, 현대적인 애플리케이션에서는 보안의 문제로 잘 사용하지 않는다.
  • formLogin : 기본 로그인 폼과 관련된 필터를 비활성화

    • 아래 form 비활성화
  • addFilterBefore : 필터의 순서를 지정해주는 역할

    • .addFilterBefore(jwtFilter, SecurityContextHolderAwareRequestFilter.class)

    • jwtFilter 뒤에 SecurityContextHolderAwareRequestFilter 를 위치

  • authorizeHttpRequests

    • 어디에 어떻게 권한 검사를 할 것인지에 대한 구체적인 내용을 작성하면 된다.
    .authorizeHttpRequests(auth -> auth
                  .requestMatchers("/api/login").permitAll()
                  .requestMatchers("/api/admin/**").hasRole("ADMIN")
                  .requestMatchers("/api/user/**").hasRole("USER")

SecurityContextHolder

  • 현재 쓰레드에서 인증된 사용자(Authentication 객체)를 저장/조회하는 Spring Security의 핵심 저장소

  • 지금 로그인한 사용자가 누구인지"를 저장하는 곳

인증 정보 저장 위치

Authentication auth = SecurityContextHolder.getContext().getAuthentication();

SecurityContextHolder.getContext().setAuthentication(authentication); //수동 저장
  • 커스텀 필터나 컨트롤러, 서비스 어디서든 SecurityContextHolder를 통해 인증 정보에 접근 가능

포인트 컴포넌트 정리

  • JwtFilter : JWT 토큰 존재/유효성 검사 → 사용자 인증 처리

  • JwtUtil : 토큰 발급, 파싱, 검증 유틸 클래스

  • SecurityContextHolder : 인증 결과 저장소 (스레드 단위)

  • Authentication : 인증 객체 (권한 포함)

  • UsernamePasswordAuthenticationToken : Spring Security에서 JWT 토큰 방식 사용하기 위함 객체

  • User : Spring Security에서 인증된 사용자 표현


Spring Security의 인가 흐름

[SecurityContext에 저장된 Authentication][FilterSecurityInterceptor][AccessDecisionManager][권한 정보와 비교 → 접근 허용 or 거부]
  • JwtFilter
    • 인증된 사용자 정보를 SecurityContextHolder에 저장
      → [Authentication 저장]
  • .authorizeHttpRequests(...)
    • 인가 정책 정의
      → 내부적으로 FilterSecurityInterceptor
      → AccessDecisionManager 자동 구성
  • authenticated()
    • 인가 조건
      → AccessDecisionManager가 인증 여부 판단

인가 방법 2가지

1) URL 기반 설정 (권장: 관리 편의성)

http.authorizeHttpRequests()
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().permitAll();
  • url 내에 **이 뒤에 무엇이 오든 상관이 없다 라는 뜻이다.

2) 메서드 기반 어노테이션 (서비스 레이어 권한 제어 {컨트롤러 레이어도 가능함})

@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) { ... }
  • 사용 전 @EnableMethodSecurity(prePostEnabled = true) 필요
    • 저 어노테이션이 있어야 활성화가 됨.

2개의 댓글

comment-user-thumbnail
2025년 12월 3일

지나가다 들렀는데 내용이 유익하네요 그리고 반존대 뭐죠

1개의 답글