전 velog글 [Spring] 9. Spring Security -1 이후의 추가 글.
https://velog.io/@kimybeom/9.-Spring-Security
//Spring Security
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'

OncePerRequestFilter의 설계 목적OncePerRequestFilter는 "한 요청당 필터가 한 번만 실행되도록 보장"하는 Spring의 특수 필터이다.
이 클래스를 상속받으면 doFilterInternal 메서드를 오버라이드해야 한다.
doFilter 메서드는 이미 OncePerRequestFilter 내부에서 구현되어 있으며, doFilterInternal을 호출하는 구조로 설계되었다.
doFilter vs doFilterInternaldoFilterFilter 인터페이스의 메서드.OncePerRequestFilter에서는 final로 선언되어 오버라이드가 불가능.doFilterInternalOncePerRequestFilter에서 제공하는 추상 메서드OncePerRequestFilter가 요청 당 한 번만 실행되도록 관리한다.doFilter를 사용할 수 없나?OncePerRequestFilter의 doFilter 메서드는 final로 선언되어 있어 오버라이드가 금지된다.public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) { ... }doFilterInternal을 구현하도록 강제하여, 개발자가 실수로 필터가 여러 번 실행되지 않도록 방지한다.@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain) throws ServletException, IOException {
String url = request.getRequestURI();
if (url.startsWith("/auth")) {
filterChain.doFilter(request, response);
return;
}
String bearerToken = request.getHeader("Authorization");
if (bearerToken == null || !bearerToken.startsWith("Bearer ")) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "JWT 토큰이 필요합니다.");
return;
}
processJwtToken(request, response, filterChain, bearerToken);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("in doFilter");
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String path = httpRequest.getRequestURI();
if (path.startsWith("/auth/") || path.equals("/actuator/health")) {
chain.doFilter(request, response);
return;
}
String bearerJwt = httpRequest.getHeader("Authorization");
if (bearerJwt == null) {
// 토큰이 없는 경우 400을 반환합니다.
httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "JWT 토큰이 필요합니다.");
return;
}
String jwt = jwtUtil.substringToken(bearerJwt);
JwtFilter와 SecurityConfig 작성 후, 추가로 할 일.
AuthUserArgumentResolver 및 관련 설정 제거→ 기존 @Auth 어노테이션을 @AuthenticationPrincipal로 교체
AuthUserArgumentResolver.java, WebConfig, FilterConfig를 삭제
→
AuthUserArgumentResolver의 역할 대체
@Auth 어노테이션을 통해 AuthUser를 컨트롤러에 주입하는 커스텀 리졸버.@AuthenticationPrincipal로 대체 가능.public ResponseEntity<?> getProfile(@AuthenticationPrincipal AuthUser authUser) { ... }
WebConfig의 중복 설정 제거WebMvcConfigurer를 통해 AuthUserArgumentResolver를 등록.@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new AuthUserArgumentResolver());
}
FilterConfig의 중복 필터 등록 문제 해결FilterRegistrationBean으로 JwtFilter를 서블릿 필터로 등록.@Bean
public FilterRegistrationBean<JwtFilter> jwtFilter() { ... }SecurityFilterChain에서 필터를 관리..addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)FilterConfig 삭제.

@Auth를 사용한 곳을 찾아 해당 컨트롤러(@Auth가 있는)의 어노테이션을 @AuthenticationPrincipal로 교체
@Auth AuthUser authUser,
@PathVariable long todoId,
@AuthenticationPrincipal AuthUser authUser,
@PathVariable("todoId") long todoId,
왜 UserDetails를 구현할까?
Authentication)의 principal로 UserDetails 타입을 기대.@AuthenticationPrincipal로 주입 받을 때도 UserDetails 타입이면 더 많은 정보(권한 등)를 활용.
Implement methods를 누르면 쫘르륵 나온다.

아니 근데 아래 4줄의 코드는 대체 무엇인가?
→ 이 네 가지 boolean 메서드는 모두 계정의 "상태"를 나타내는 용도
1. isAccountNonExpired()
2. isAccountNonLocked()
3. isCredentialsNonExpired()
4. isEnabled()
JWT 기반 인증에서는 주로 모두 true로 고정해서 사용.
계정 상태 관리가 필요하면, DB 값에 따라 true/false를 반환.