포스트

05.JWT검증필터

JWTFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import com.example.userservice.dto.MyUserDetails;
import com.example.userservice.model.User;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@RequiredArgsConstructor
@Component
public class JWTFilter extends OncePerRequestFilter {
        // OncePerRequestFilter 는 요청에 대해 한번만 동작한다

  private final JWTUtil jwtUtil;

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

    // request의 해더에 담아진 토큰 정보를 해석하여 유저 정보를 가져와야 한다
    // 이를 JWTUtil이 진행한다

    // 토큰 검증 구현
    // request에서 Authorization 헤더를 찾는다
    String authorization = request.getHeader("Authorization");

    // request 헤더에서 가져온 authorization이 null인지 혹은 Bearer로 시작하는지 확인
    if(authorization == null || !authorization.startsWith("Bearer ")){
        System.out.println("token null");
        
        // filterChain으로 엮여있는 다음 필터에 doFilter를 통해 request와 response를 넘겨준다
        filterChain.doFilter(request, response);

        // 조건에 해당되면 메소드 종료 (필수)
        return;
    }

    // 토큰을 분리하여 소멸 시간 검증
    String token = authorization.split(" ")[1];
    // Bearer 와 뒤의 토큰 정보를 분리하고, 토큰 정보를 받아옴

    // JWTUtil에 구현한 유효기간 검사
    if(jwtUtil.isExpired(token)){

        System.out.println("token expired");

        // filterChain으로 엮여있는 다음 필터에 doFilter를 통해 request와 response를 넘겨준다
        filterChain.doFilter(request, response);

        // 조건에 해당되면 메소드 종료 (필수)
        return;
    }

    // 여기까지 왔으면, 토큰이 있고, 양식도 제대로 되어있고 유효기간도 남아있다는 것
    // 토큰을 기반으로 일시적인 세션을 만들어서 저장한다.
    // 토큰에서 username과 role을 획득한다

    String username = jwtUtil.getUsername(token);
    String role = jwtUtil.getRole(token);

    // User Entity에 값을 설정하여 생성
    User user = new User();
    user.setEmail(username);
    user.setPassword("tempPassword");
    user.setRole(role);
    // 패스워드의 경우 토큰에 담겨있지 않았는데, 비밀번호도 초기화를 해줘야 한다
    // 비밀번호를 DB에서 매번 요청이 올 때마다 조회를 하면 비효율적이므로 임시로 만들어 초기화

    // MyUserDetails 객체를 만든다.
    MyUserDetails myUserDetails = new MyUserDetails(user);

    // 스프링 시큐리티에 인증 토큰 생성
    Authentication authentication = new UsernamePasswordAuthenticationToken(myUserDetails, null, myUserDetails.getAuthorities());

    // 세션에 사용자 등록
    SecurityContextHolder.getContext().setAuthentication(authentication);

    filterChain.doFilter(request, response);

    // 검증 필터를 만들었다면 SecurityConfig에 가서 필터를 등록해주자 
  }
}
  • 스프링 시큐리티 filter chain에 요청에 담긴 JWT를 검증하기 위한 커스텀 필터
  • 해당 필터를 통해 요청 헤더 Authorization에 담긴 JWT를 검증하고 강제로 SecurityContextHolder에 세션을 임시로 생성
  • 해당 세션은 STATELESS 상태로 관리되므로 해당 요청이 끝나면 소멸된다
1
2
3
4
5
6
7
8
// 토큰 검증 필터 등록
        http
                .addFilterBefore(new JWTFilter(jwtUtil), LoginFilter.class);

        LoginFilter loginFilter = new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil);
        loginFilter.setFilterProcessesUrl("/api/users/loginProc");
        http
                .addFilterAt(loginFilter, UsernamePasswordAuthenticationFilter.class);
  • 로그인 필터 앞에 필터 등록

  • 로그인 진행 후 응답 헤더에 Authorization 토큰 정보 확인
  • 다른 서비스 요청 시 요청 헤더에 Authorization으로 토큰 정보 전달
참고 사이트

개발자 유미