사용자 지정 JWT 필터 구현하기
요청을 가로채고 JWT를 추출하여 사용자를 인증하는 사용자 지정 `OncePerRequestFilter`를 만듭니다.
사용자 지정 JWT 필터 구현하기은(는) CoddyKit의 무료 Spring Security 6 & JWT Authentication 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Spring Security 6 & JWT Authentication 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Spring Security 6 & JWT Authentication 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
Why a Custom JWT Filter?
When using JSON Web Tokens (JWTs) for authentication, Spring Security doesn't inherently know how to process them. This is where a custom filter comes in!
A custom JWT filter intercepts incoming requests to:
- Extract the JWT.
- Validate its authenticity and expiration.
- Inform Spring Security about the authenticated user.
The OncePerRequestFilter Base
For our custom JWT filter, we'll extend Spring's OncePerRequestFilter. This base class guarantees that your filter logic runs exactly once per HTTP request, preventing redundant processing.
- It simplifies filter implementation.
- Ensures efficiency for each request.
- It's ideal for authentication logic.
Basic Filter Structure
Every custom filter extending OncePerRequestFilter must override the doFilterInternal method. This is where our JWT processing logic will reside.
It provides access to the HttpServletRequest, HttpServletResponse, and the FilterChain to pass the request along.
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain
) throws ServletException, IOException {
// Our JWT logic will go here
filterChain.doFilter(request, response);
}
}Extracting the JWT
The first step in our filter is to extract the JWT from the incoming request. JWTs are typically sent in the Authorization header, prefixed with Bearer .
We need to check for this header and remove the 'Bearer ' part to get the raw token.
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain
) throws ServletException, IOException {
final String authHeader = request.getHeader("Authorization");
String jwt = null;
if (authHeader != null && authHeader.startsWith("Bearer ")) {
jwt = authHeader.substring(7); // Remove "Bearer " prefix
System.out.println("Extracted JWT: " + jwt.substring(0, 5) + "...");
} else {
System.out.println("No JWT found or invalid header.");
}
filterChain.doFilter(request, response);
}
}Validating & Parsing (Concept)
After extraction, the JWT must be validated. This involves:
- Signature verification: Ensuring the token hasn't been tampered with.
- Expiration check: Confirming the token is still valid.
- Claim extraction: Retrieving information like the username (subject) from the token's payload.
This validation is typically handled by a dedicated JwtService or similar utility, which our filter would use.
Loading User Details (Concept)
Once the JWT is validated and the username (or user ID) is extracted, the filter needs to load the user's specific details. This is usually done using Spring Security's UserDetailsService.
The UserDetailsService fetches the UserDetails object, which contains information like roles and authorities needed for authorization.
Setting Authentication Context
The final crucial step is to inform Spring Security that the user is authenticated for the current request. This is done by creating an Authentication object (e.g., UsernamePasswordAuthenticationToken) and setting it in the SecurityContextHolder.
Once set, Spring Security will recognize the user as authenticated for the duration of the request.
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
// Simulate a UserDetails object loaded from DB
UserDetails userDetails = new User(
"exampleUser", "[PASSWORD_HASH]", new ArrayList<>()
);
// Create an Authentication token
UsernamePasswordAuthenticationToken authToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities()
);
// Set the Authentication in the SecurityContextHolder
SecurityContextHolder.getContext().setAuthentication(authToken);
System.out.println("Authentication set for: " +
SecurityContextHolder.getContext().getAuthentication().getName());
// Clear context after request (or for next test)
SecurityContextHolder.clearContext();
}
}Runnable Filter Logic Example
Let's see a simplified, runnable example that mimics the core logic of our custom JWT filter. It simulates extracting a token and setting authentication based on its presence.
This example uses mock components to show the flow without a full Spring Boot setup.
import java.util.HashMap;
import java.util.Map;
// Simulate Spring Security components needed for the filter logic
class MockSecurityContextHolder {
private static String authenticatedUser = null;
public static String getAuthentication() {
return authenticatedUser;
}
public static void setAuthentication(String user) {
authenticatedUser = user;
}
public static void clearContext() {
authenticatedUser = null;
}
}
public class Main {
// This method simulates the core logic of doFilterInternal
public static void simulateJwtFilter(Map<String, String> requestHeaders) {
System.out.println("--- Simulating JWT Filter Logic ---");
final String authHeader = requestHeaders.get("Authorization");
String jwt = null;
String username = null;
if (authHeader != null && authHeader.startsWith("Bearer ")) {
jwt = authHeader.substring(7);
// In a real app, this extracts username from JWT payload
username = "coddyuser"; // Simplified: assume "coddyuser" from a valid JWT
System.out.println("Filter: JWT found: " + jwt.substring(0, 5) + "...");
} else {
System.out.println("Filter: No JWT found or invalid header format.");
}
// Check if user is already authenticated
if (username != null && MockSecurityContextHolder.getAuthentication() == null) {
// Simulate JWT validation (signature, expiration, etc.)
boolean tokenValid = true; // Assume valid for this simple demo
if (tokenValid) {
// In a real filter, we'd load UserDetails and create an Authentication object
MockSecurityContextHolder.setAuthentication(username);
System.out.println("Filter: User '" + username + "' authenticated and context set.");
} else {
System.out.println("Filter: JWT validation failed.");
}
} else if (username != null && MockSecurityContextHolder.getAuthentication() != null) {
System.out.println("Filter: User '" + username + "' already authenticated for this request.");
}
// In a real filter, this would call filterChain.doFilter(...)
System.out.println("Filter: Request passed to next filter/handler.");
System.out.println("--- Filter Logic End ---");
}
public static void main(String[] args) {
// Scenario 1: Request with a valid JWT
Map<String, String> headers1 = new HashMap<>();
headers1.put("Authorization", "Bearer abc.def.ghi");
simulateJwtFilter(headers1);
System.out.println("Main: SecurityContext has authentication: " +
(MockSecurityContextHolder.getAuthentication() != null ? MockSecurityContextHolder.getAuthentication() : "None"));
MockSecurityContextHolder.clearContext(); // Clean up
System.out.println("\n");
// Scenario 2: Request without a JWT
Map<String, String> headers2 = new HashMap<>();
simulateJwtFilter(headers2);
System.out.println("Main: SecurityContext has authentication: " +
(MockSecurityContextHolder.getAuthentication() != null ? MockSecurityContextHolder.getAuthentication() : "None"));
MockSecurityContextHolder.clearContext(); // Clean up
}
}Integrating with Spring Security
After creating our JwtAuthenticationFilter, we need to register it with Spring Security's filter chain. This is typically done in your security configuration class (e.g., SecurityConfig) using HttpSecurity.
We use .addFilterBefore() to ensure our JWT filter runs before Spring Security's default authentication filters, like UsernamePasswordAuthenticationFilter.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
// Assuming JwtAuthenticationFilter is defined elsewhere
class JwtAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, jakarta.servlet.FilterChain filterChain) throws jakarta.servlet.ServletException, java.io.IOException {
// Simplified for config demo
filterChain.doFilter(request, response);
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthFilter;
// Inject our custom JWT filter
public SecurityConfig(JwtAuthenticationFilter jwtAuthFilter) {
this.jwtAuthFilter = jwtAuthFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // Disable CSRF for stateless APIs
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll() // Public endpoints
.anyRequest().authenticated() // Secure all other requests
)
// Add our custom JWT filter BEFORE the default UsernamePasswordAuthenticationFilter
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}Filter Chain Challenge
Consider the JwtAuthenticationFilter we've discussed. What are its key responsibilities in the Spring Security filter chain when handling JWT-based authentication?
Lesson Recap: Custom JWT Filter
In this lesson, we explored how to build a custom OncePerRequestFilter to handle JWT authentication in Spring Security.
- We learned to extract JWTs from the
Authorizationheader. - We understood the conceptual steps of validating the token and loading user details.
- We saw how to authenticate the user by setting the
SecurityContextHolder. - Finally, we covered how to integrate this filter into Spring Security's filter chain using
HttpSecurity.addFilterBefore().
This custom filter is crucial for integrating JWTs seamlessly into your secure Spring Boot applications.
자주 묻는 질문
“사용자 지정 JWT 필터 구현하기” 강의는 무료인가요?
네 — “사용자 지정 JWT 필터 구현하기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Spring Security 6 & JWT Authentication 강의 전체를 잠금 해제할 수 있습니다. Spring Security 6 & JWT Authentication 강의에는 총 4개의 강의가 포함되어 있습니다.
“사용자 지정 JWT 필터 구현하기”에서 뭘 배우나요?
요청을 가로채고 JWT를 추출하여 사용자를 인증하는 사용자 지정 `OncePerRequestFilter`를 만듭니다. 브라우저에서 직접 실행하는 실습 코드로 Spring Security 6 & JWT Authentication을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
Spring Security 6 & JWT Authentication을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 Spring Security 6 & JWT Authentication은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.
“사용자 지정 JWT 필터 구현하기” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 Spring Security 6 & JWT Authentication 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 Spring Security 6 & JWT Authentication 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- JWT 인증 흐름 설계하기
- 사용자 지정 JWT 필터 구현하기
- AuthenticationManager 및 Provider 통합
- 인증 오류와 진입점 처리하기