Spring Security에서 토큰 검증 과정
검증 흐름
먼저 제가 작성한 코드가 어떤식으로 검증이 되는지 보겠습니다.
로그인 성공
토큰 생성
로그인 성공 -> 토큰을 생성하는 로직입니다.
토큰 검증
토큰을 검증하는 흐름을 보겠습니다.
getUsername()
여기까지 로그인 후 토큰을 생성하고, 그 토큰을 검증하는 과정입니다.
이제부터 이 흐름에 왜 의문을 품었는지더 좋은 방법을 하나 하나 생각한 내용을 적겠습니다.
의문 발생
토이프로젝트를 하다가 받은 리뷰입니다.
인터넷과 강의에있는 내용을 그대로 받아들인 후 토큰 검증 과정을 구현했었는데
이러한 리뷰를 통해 왜 이렇게 해야 되는건지 고민하는 계기가 되었습니다.
DB에 접근하지 않아도 User객체를 생성한다거나, 권한 정보만 들고와서 ContextHolder에 저장하는 방법을 생각해봤습니다.
지금부터 제가 생각한 방법과 문제점에 대해 서술하겠습니다.
1. 토큰에 User객체 넣기
첫번째 생각한 방법입니다. 토큰에 username과 id를 넣는게 아닌 객체를 넣고 사용하면 DB접근도 필요 없고 더 편하게 코드를 작성할 수 있다고 생각했습니다.
Jwt payload에 Entity 직접 넣고 빼는 메서드를 작성했습니다.
이러한 것을 바탕으로 doFilterInternal() 메서드에서 직접 확인해보겠습니다.
문제 발생) payload에 Entity를 직접 넣을 수 없습니다.
이 문제는 JWT 라이브러리가 객체를 직렬화하고 역직렬화하는 방식에 기인합니다. JWT 라이브러리는 Java 객체를 직접적으로 JWT payload에 넣을 수 없으며, 객체를 직렬화하여 문자열로 변환한 후 payload에 포함해야 합니다. 일반적으로 JSON 형식으로 직렬화된 문자열을 사용합니다.
따라서 User 객체를 JWT payload에 넣고 싶다면, User 객체를 JSON 형식으로 직렬화하여 JWT payload에 추가해야 합니다. JWT 라이브러리에서 JSON 직렬화를 수행하는 방법은 라이브러리에 따라 다를 수 있으므로, 해당 라이브러리의 문서를 참조하여 직렬화를 수행하는 방법을 확인해야 합니다.
예를 들어, Gson 라이브러리를 사용하여 User 객체를 JSON 문자열로 직렬화하고 JWT payload에 추가하는 방법은 다음과 같을 수 있습니다:
해결 방법) Gson 라이브러리 사용
Gson 라이브러리를 사용해서 해결하는 방법입니다.
메서드부터 다 바뀝니다.
1. payload 값에 User 객체를 JSON 문자열로 직렬화 후, 값을 담아줍니다.
2. JSON 문자열로 받은 값을 역질렬화 해줍니다.
좋은 방법이 아닌 이유
보안상의 이유로 JWT의 payload는 주로 문자열이나 기본 자료형의 값을 가지는 것이 좋습니다.
Entity 객체를 직접적으로 JWT payload에 넣는 것은 권장되지 않습니다.
보안 문제: Entity 객체는 일반적으로 애플리케이션의 도메인 모델을 나타내기 때문에 민감한 정보를 포함하고 있을 수 있습니다. JWT는 클라이언트에게 전송되는 것이므로, 토큰이 탈취되었을 때 클라이언트가 Entity 객체를 역직렬화하여 액세스할 수 있는 상황이 발생할 수 있습니다. 이는 보안상의 위험을 초래할 수 있습니다.
그리고 이렇게까지 하는 방법이 너무 복잡하고 비효율적이라고 판단했습니다.
2. getUsername()이 아닌 Authentication을 이용
두번째 방법입니다.
username이나 userId 값이 아닌 Authentication을 바로 리턴 받는 방식입니다.
TokenProvider 클래스 안에 새로 만든 메서드입니다.
토큰이 유효하다면 바로 SecurityContextHolder에 Authentication 객체를 넣는 방식입니다.
이 방식도 결국 DB를 조회한다.
이 값을 이용하여 Controller -> Service -> Repository까지 가는 과정입니다.
게시글 생성을 예로 들겠습니다.
Controller입니다.
Service입니다.
Post와 User는 ManyToOne, OneToMany로 매핑이 되어있기 때문에 DB조회를 통해 User 객체를 얻어야 합니다.
결론
개인적인 생각입니다.
payload에 Entity를 넣는 것만 아니면 두 방식 다 괜찮다고 생각합니다.
doFilterInternal()에서 DB에 접근해 User 객체를 얻는 것이나, Sevice에서 User 객체를 얻는 것이나 결국 똑같다고 생각합니다.
중요한 내용은 아니였지만 어떤게 더 효율적인 방법인지 궁금해서 혼자 학습한 내용입니다.
여기서 그치지 않고 개발을 진행하면서 좋은 방법이 생각나거나 잘못된 부분을 발견하면 수정하겠습니다.
'Spring > Security' 카테고리의 다른 글
OAuth2) 싱글벙글 OAuth2, 그리고공식문서를 보고 하나하나 값 받아오기 (0) | 2023.07.11 |
---|---|
Spring Security) Spring Boot 3.1.0, Spring 6.1, Spring Security 6.1 (0) | 2023.06.29 |
Spring Security) UserDetailsService에서 비밀번호는 어디에서 검사하는 걸까? (0) | 2023.02.24 |