티스토리 뷰

∙Java & Spring

JWT 구조

coor 2022. 4. 16. 18:17

들어가며

새로운 프로젝트를 하면 첫 번째 기능이 JWT Token 활용해서 만드는 로그인, 로그아웃 페이지이다.
항상 할 때마다 흐름과 각 클래스마다 역할을 까먹어서 중간중간 구글링 하는 것보다 정리하는 게 낫다고 생각이 들어서
정리하기로 마음 먹었다. 일단 스프링 부트에서 제공하는 Security와 Jwt 토큰에 대해 알아보자

 

1. Security 역할

웹 사이트에서 관리자 페이지와 사용자 페이지가 필수이다. 
그로 인해서 특정 페이지를 조회와 데이터 요청을 권한에 따라 주거나 막아야 한다.
해커가 관리자 페이지에 들어가서 정보를 약탈하거나 게시글을 삭제하면 아주 큰 문제가 발생한다.
SpringBoot Security는 이러한 부분을 제어할 수 있도록 도와주는 역할을 한다.

 

 

2. JWT 구조

구조는 3개로 구성되어 있으며, 제일 중요한 건 서명이다. 
서명에 있는 비밀키는 자기가 구성하는 키로 절대 노출되어선 안된다.
쿠키와 세션 방식과 다르게 토큰은 만료시간이 있어서 그 시간이 지나면 토큰은 사용할 수 없다.

하지만 토큰 방식도 단점이 있다. 
토큰은 클라이언트한테 보내고 서버에서 관리가 할 수 없다.
예를 들어 해커가 토큰을 가져가면 토큰은 삭제하던가 만료시간은 제어해야 되지만 그럴 수 없다.
또한 사용자가 PC에서 로그인하고 또 모바일에서 로그인하는 경우 토큰을 없앨 수 없다.
이러한 문제점으로 인해서 두 개의 토큰을 발행한다.
한 개는 만료시간이 짧은 access_token
다른 한 개는 만료시간이 긴 refresh_token

access_token을 가지고 페이지를 요청하거나 로그인을 유지할 수 있다. 
만약 만료시간이 근접하면 refresh_token을 통해 access_token 새로 생성한다.
refresh_token는 클라이언에서 안전한 곳으로 보관하고 서버에서는 DB에 저장한다. - 클라이언트 역할

 

3. JWT 흐름

주관적인 코드의 흐름 순서이다. 

- 로그인 흐름

로그인을 시도하면 필터에 접근한다.
현재 토큰과 관련된 영향이 없으므로 filterChain.doFilter(request, response) 실행한다.

클라이언트가 form 인증 방식으로 최초로 인증을 시도하게 되면 UsernamePasswordAuthenticationFilter가
인증 처리를 하게 되고 인증이 성공하면 SecurityContext에 Authentication 객체를 저장하고 인증처리는 완료된다.
Authentication 객체에는 인증 성공 결과(User, Authorities) 저장된다.

만약 정보가 일치하지 않는다면 MessageResponse 에러 메시지가 클라이언트로 보내진다.

시큐리티 인증 객체에 넣기 위해서 SecurityContextHolder(authentication) 넣는다.
Authentication 객체를 통해 인증을 했는지, 어떤 권한이 있는지, IP는 어떤 건지 확인할 수 있다. 

Credentials=[PROTECTED]
Authenticated=true
Details=WebAuthenticationDetails
[RemoteIpAddress=0:0:0:0:0:0:0:1
SessionId=null]
Granted Authorities=[ROLE_ADMIN]]

실제 조회한 값이다. 이제 저 권한을 통해 관리자와 사용자를 나눌 수 있고 특정 페이지 URL과 중요 데이터 정보를 권한에 따라 값을 줄 수 있다. 

마지막으로 토큰을 발행하는 코드이다.
자신이 설정한 비밀키과 만료시간을 따로 파일에 저장해두고 .gitignore에 파일 이름을 넣어서 중요한 정보들은 넣지 않아야 한다. Jwt 토큰을 발행할려면 라이브러리를 다운로드하여야 한다.

implementation 'io.jsonwebtoken:jjwt:0.9.1'

 

- 토큰 유효 확인

 

 

4. SessionCreationPolicy.STATELESS 의 뜻

.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)​

"세션 방식의 인증 처리를 하지 않겠다."
처음에는 이게 무슨 소리인지 몰라서 구글링을 많이 했다.
쉽게 생각하면 토큰의 특성상 매번 인증을 받아야 하고 인증을 필요로 하는 상태가 되어야 한다.

인증에 성공한 이후에 SecurityContext 객체를 세션에 저장하는 역할을 하는 AuthentiationJwtTokenFilter는  SecurityContext 객체를 세션에 저장하지 않는다. 정확히 표현하면 SessionCreationPolicy.STATELESS 설정으로 인해 세션 존재 여부를 떠나서 세션을 통한 인증 메커니즘 방식을 취하지 않는다. 

그렇기 때문에 인증에 성공한 이후라도 클라이언트가 다시 어떤 자원에 접근을 시도할 경우 SecurityContextPersistenceFilter는 세션 존재 여부를 무시하고 상 새로운 SecurityContext 객체를 생성하기 때문에 인증 성공 당시 SecurityContext에 저장했던 Authentication 객체를 더 이상 참조할 수 없게 되어버린다.

그렇기 때문에 매번 인증을 받아야 하고 인증을 필요로 하는 상태가 됩니다.

즉 세션 방식의 인증 처리가 되지 않는 것이다.

SessionCreationPolicy.STATELESS는 스프링 시큐리티가 인증 메커니즘을 진행하는 과정에서 세션을 생성하지 않는다는 것이고 혹 이미 이전에 세션이 존재한다고 하더라도 그 세션을 사용하여 세션쿠기 방식의 인증처리를 하지 않겠다는 의미이다. 

SessionCreationPolicy.STATELESS 설정이 스프링 시큐리티가 무조건 세션을 생성하지 않는다는 의미보다는 인증 처리 관점에서 세션을 생성하지 않음과 동시에 세션을 이용한 방식으로 인증을 처리하지 않겠다는 의미로 해석할 수 있다는 점이고 인증 메커니즘이 아닌 다른 특정한 곳에서 세션과 관련된 처리를 해야 하는 경우나 스프링이 아닌 다른 애플리케이션에서 세션을 사용하는 곳이 있다면 세션이 생성될 수도 있다는 점이다.

 

'∙Java & Spring' 카테고리의 다른 글

ThreadPoolTaskExecutor 이용하여 성능 개선하기  (0) 2022.05.29
Junit Mock 사용하기  (0) 2022.05.06
인프런 스프링 핵심 원리 정리  (0) 2022.04.20
자바의 정석 정리(2)  (0) 2022.03.09
자바의 정석 정리(1)  (0) 2022.03.02