12.15 - TIL Spring Security + JWT

이서준·2025년 12월 15일

SpringSecurity

목록 보기
2/4

Spring Security

특징

  • Spring Security는 세션 기반
  • 로그인 → 서버가 세션 생성
  • 이후 요청 → JESSIONID로 인증 상태 유지
  • 서버가 기억해야 할 것이 많음

JWT

  • 로그인 → 토큰 발급
  • 이후 요청 → 토큰을 헤더에 담아 보냄
  • 토큰만 검증하므로 서버는 기억 안함
  • Stateless

Spring Security + JWT 흐름

1. 로그인

  • 아이디/비밀번호 검증
  • 성공 시 JWT 발급
  • 응답으로 Access Token 반환

2. 이후 요청

Client
 ✅ JWT
 -> JwtFilter
 -> Spring Security Filter Chain
 -> Controller
  • JwtFilter에서 JWT가 있으면 검증 없으면 지나가게 함

SecurityConfig 역할

.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
  • 기존 로그인 필터는 끔
  • JWT Filter를 Security 필터 체인에 편입
  • 모든 요청은 JwtFilter를 거침
.authorizeHttpRequests(auth -> auth
    .requestMatchers("/api/users", "/api/auth/login").permitAll()
    .anyRequest().authenticated()
)
  • 로그인/회원가입은 토큰 없이 허용
  • 나머지는 SecurityContext에 Authentication이 있어야 통과

JwtFilter 흐름

Long userId = jwtUtil.extractUserId(token);
String username = jwtUtil.extractUsername(token);
UserRole role = jwtUtil.extractUserRole(token);

UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

SecurityContextHolder.getContext().setAuthentication(authentication);
  1. Authorization 헤더 확인
  2. Bearer 토큰 없으면 그냥 통과
  3. 있으면 토큰 검증
  4. 토큰에서 사용자 정보 추출
  5. Authentication 생성
  6. SecurityContextHolder에 저장

UserDetails

Spring Security에서 사용자의 세부 정보를 담는 인터페이스
주로 사용자 이름, 비밀번호, 권한 등의 정보를 제공

  • @AuthenticationPrincipal 사용
  • @PreAuthorize 사용
  • hasRole, hasAuthority 사용
//Controller에서 사용 가능 null이 아님
@AuthenticationPrincipal UserinfoDetails user

코드

SpringSecurity

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {

	private final JwtFilter jwtFilter;
	private final CustomUserDetailService customUserDetailService;
  private final CustomAccessDeniedHandler customAccessDeniedHandler;
  private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
						.csrf(AbstractHttpConfigurer::disable)
            .httpBasic(AbstractHttpConfigurer::disable)
            .formLogin(AbstractHttpConfigurer::disable)
            .cors(cors -> cors.configurationSource(corsConfigurationSource()))
            .userDetailsService(customUserDetailService)
            .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
            .exceptionHandling(e ->
                 e.authenticationEntryPoint(customAuthenticationEntryPoint)
                  .accessDeniedHandler(customAccessDeniedHandler)
             )
             .authorizeHttpRequests(auth -> auth
                   .requestMatchers("/api/users", "/api/auth/login").permitAll()
                   .anyRequest().authenticated()
              )
              .build();
  }
}

JwtFilter

@Slf4j
@Component
@RequiredArgsConstructor
public class JwtFilter extends OncePerRequestFilter {

  private final JwtUtil jwtUtil;

  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

		String authorizationHeader = request.getHeader("Authorization");

		if (authorizationHeader == null || !authorizationHeader.startsWith(BEARER_PREFIX)) {
			filterChain.doFilter(request, response);
			return;
		}

		String token = authorizationHeader.substring(7);

		if (!jwtUtil.validateToken(token)) {
			filterChain.doFilter(request, response);
			return;
		}

		Long userId = jwtUtil.extractUserId(token);
		String username = jwtUtil.extractUsername(token);
		UserRole role = jwtUtil.extractUserRole(token);

		UserinfoDetails userDetails = new UserinfoDetails(userId, username, role);

		UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

		SecurityContextHolder.getContext().setAuthentication(authentication);

		filterChain.doFilter(request, response);
  }
}

정리

역할하는 곳
로그인Controller, Service
JWT 발급Service, Util
JWT 검증JwtFilter
인증 객체 저장SecurityContextHolder
인가SpringSecurity
세부 정보UserDetails
profile
Allons-y

0개의 댓글