리프레시 로직 제외, JWT 사용할때 크게 두가지로 분류함.
1. 로그인 및 JWT 발급
2. 요청에 포함된 JWT 토큰 검증
1단계에서는 DB에 유효한 사용자가 존재하는지, 비밀번호 또는 인증정보가 일치하는지 검증을 위해서 DB 접근이 필수적
-> loadUserByUsername으로 유저 정보를 가져와서 존재할 경우 UserDetails를 생성하고 리턴하는게 맞음. 이후 비밀번호 확인 등을 수행함.
2단계에서는 JWT 토큰에 적절한 인증 정보가 포함되어 있다면, 검증 필터에서 DB에 접근할 필요가 없어짐.
시큐리티 설정에서 addFilterBefore, addFilterAfter로 인증 필터 순서를 결정해 줄 수 있는데 일반적으로는 구조 상
요청 -> JWT검증 필터 -> 로그인 필터 -> JWT 생성 필터 -> 로깅 필터, 인터셉터 등등.. 으로 커스텀 필터를 구성하게 됨.
요청이 들어오면, 먼저 요청 Authorization 헤더에 JWT가 포함되어 있는지 확인하고, 검증을 시도함.
-> 검증 과정에서는 특별한 이유가 없다면 DB에 접근할 필요가 없음. 분산 환경처럼 반드시 JWT를 써야만 하는 이유가 없고, 검증 과정에서 DB 접근이 요구된다면 차라리 세션을 쓰는게 낫다.
세션은 한번 인증이 성공하고 Context가 생성되면 해당 컨텍스트 내부에 Authentication 객체, UserDetails객체를 저장하도록 만들 수 있고, 세션 만료 전까지 저장이 됨.
없다면, 로그인 필터가 작동하고, 로그인 성공 이후 Auth 객체를 생성해서 설정해줌. 이후에 JWT생성 필터에서 ContextHolder에 Context.getAuth가 존재하는지 확인하여 존재한다면 JWT 토큰을 발급하게 됨.
이때 shouldNotFilter 메소드로 path 패턴매칭을 통해서 로그인 하는 경우에만 발급하도록 하여 다른 URI에서는 기존 토큰을 대체하지 않도록 처리함.
---
서명 정보 기반으로 JWT 검증 성공 시 JWT 내에 포함된 식별정보를 기반으로 Authentication을 구현하는 객체를 생성하여 Sec.urityContextHoder.getContext().setAuthentication(auth); 를 설정해주면
이후 뒤쪽에 위치한 필터에서는 필터 메소드가 실행되긴 하지만 Auth 객체가 존재하므로 별 다른 로직 없이 다음 단계로 넘어감.
인증 객체는 JWT내에 있는 username, role 등을 기반으로 생성하기에 일반적인 경우에 별도의 DB접근이 필요하지 않음. Service레이어에서 필요한 경우에 선택적으로 유저 정보와 관련한 DB접근을 수행하면 되는것이고, 매 JWT검증마다 DB에 접근하는 블로그 코드는 조금 주의할 필요가 있을듯.
JWT에 포함된 식별정보 기반으로 Auth 객체를 생성하고, 이후 컨트롤러나 서비스 레이어에서 이를 기반으로 로직을 처리할 수 있기 때문에 일반적인 경우에는 JWT 토큰 검증 시 DB 접근을 필요로 하지 않음.
---
추가적으로 Sec.urityContextHolder는 스레드 로컬 기반으로 작동하기 때문에 요청이 끝나면 사라짐.
이때 스프링 시큐리티에서 JWT와 세션의 경우 Sec.urityContextHolder는 매번 요청마다 생성되고 사라지는것은 동일함. Context의 유지 차이인데,
JWT는 매 요청마다 holder와 context를 생성함.세션의 경우 Holder가 생성되는것은 동일한데, 세션 쿠키 기반으로 세션에서 Context를 찾아서 Holder에 새롭게 setContext로 바꿔준다는 정도의 차이점임.
한줄요약: 단순한 로그인 처리 필요한거면 그냥 세션 써라
댓글 0