JWT( JSON Web Token )는 JSON 형식으로 클라이언트와 서버 간에 인증 정보를 안전하게 전달하기 위해 사용하는 토큰 기반 인증 방식입니다.
JWT는 크게 header,payload,signature로 나뉩니다.
header: signature를 암호화할 알고리즘으로써 일반적으로
*HS256
* 알고리즘이 많이 사용됩니다.
payload: 사용자의 정보를 담고 있으며 토큰 제목,토큰 대상자, 발급시각, 만료시각,유저의 id등을 담고 있습니다.
signature: header와 payload를 합친 문자열을 암호화 알고리즘과 비밀키를 이용해서 암호화 한 것입니다.→중간에 변조를 막고, 토큰이 유효한 서버로부터 발급된 것임을 인증됩니다
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5ODc2NTQzMjEwIiwibmFtZSI6IkphbmUgRG9lIiwiaWF0IjoxNjI1MjM5MDIyfQ.abcd1234efgh5678ijkl9012mnop3456qrst7890uvwx1234yzab5678cdef9012
https://datatracker.ietf.org/doc/html/rfc7519#section-1
“JWT는 HTTP의 Authorization Header나 URI Query Parameters와 같은 공간이 제한된 환경을 위한 간결한 클레임 표현 형식입니다.” 라고 소개하고 있습니다.
기본적으로 jwt를 활용한 spring security는 아래와 같은 아키텍쳐를 따릅니다.
(위 그림에서 token검증이 Authentication Filter에 들어가야한다고 봄)
로그인시에 jwt를 발급 후, client단에 넘겨주고 저장.
로그인한 클라이언트는 그 후의 작업을 할 때, API화 함께 Authorizaion header에 jwt를 담아서 넘겨줌.
그 후 AuthenticationFilter에서 통해서, 토큰이 유효한지를 검증
token이 유효하면, 해당 토큰을 통해서 사용자의 id를 파싱
파싱한 id를 통해서 userDetail객체를 생성하고, 이 객체의 정보와 인가 정보를 담고 있는 UsernamePasswordAuthenticationToken
을 생성
생성한 토큰을 Spring Context에 보관.
기본적으로 Client→Fliter→DispatcherServlet→ Controller 순으로 동작하여 spring security가 동작합니다.
제일 먼저 실행되는 JwtAuthFilter내의 doFilter에서는 토큰이 유효한지를 검사합니다
아래는 토큰이 유효한지를 검사하는 메서드이며
이 메서드를 통해서 토큰이 변조 되었는지, 만료되었는지, 등을 검증합니다.
검증된 토큰을 통해서 id값을 파싱합니다.
파싱한 id값,userDetailService를 통해서 user객체를 생성합니다
생성한 객체를 통해서 SecurityContextHolder에 담을 UsernamePasswordAuthentication token
을 생성합니다
생성한 후 ContextHolder에 담슴니다.
public String getPhoneNumberByToken(HttpServletRequest request){
String phoneNumber = "";
String authorizationHeader = request.getHeader("Authorization");
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
String token = authorizationHeader.substring(7);
if (jwtUtils.validateToken(token)) {
,,,중략
만약 SecurityContextHolder에 해당 정보가 없다면 다음과 같이 받은 request에 대해서 다시 유효한 token인지를 검증하고, id값을 parsing하는 과정을 다시 거쳐야합니다.
그러나 SecurityContextHolder
에 담으면 아래와 같이 작성이 가능합니다.
CustomUserDetails userDetails= (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
SecurityContextHolder.getContext().getAuthentication()
을 호출하면 현재 인증된 사용자의 정보를 포함하는 Authentication
객체를 가져올 수 있습니다.UserDetails
가 저장되어 있습니다getPrincipal()
:인증된 사용자 객체를 반환합니다. 일반적으로 UserDetails
객체가 반환되며, 이를 통해 사용자 정보를 확인할 수 있습니다(@AuthenticationPrincipal CustomUserDetails userDetails)
CustomUserDetails userDetails= (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()
대신 @AuthenticationPrincipal
을 이용하면 현재 인증된 사용자의 정보를 가지고 올 수 있습니다HandlerMethodArgumentResolver
를 통해서 SecurityContext에 저장된 userDetails를 주입시켜줍니다.이번 프로젝트에서 사용한 jwt방식의 구조,jwt의 원리 그리고 spring security와 관련한 기본적인 원리에 대해서 살펴보았습니다. 그러나 spring security내의 filterchain,secutirycontextholder에서 어떻게 관리 되는지,cors,csrf의 깊은 이야기는 아직 다루지 못하였습니다. 추후에 spring security에 대해서 조금더 깊은 이야기로 포스팅 하도록 하겠습니다.