본문 바로가기

Java & Spring

OAuth2.0 + Spring Security + JWT

프로젝트를 진행하면서 소셜 로그인(OAuth2.0), Spring Security, JWT Token 기능을 접목시켜 구현하기로 시도해 봤는데, 개념이 확실하게 잡히지 않은 채로 구현을 하려다 보니 소요시간이 오래 걸리기도 했고, 확실하게 이해하기 위해 따로 정리를 해봤다.

 

전체 동작과정 요약

  1. 사용자의 유저 에이전트(User Agent)가 클라이언트(Client)의 인증 시작 API를 호출한다.
  2. 클라이언트는 유저 에이전트를 카카오의 인증 URI(Authorization URI)로 리다이렉트한다.
  3. 사용자는 로그인을 수행한다.
  4. 인증 서버(Authorization Server)가 인증 성공 여부를 클라이언트로 전달한다. 성공 시 인증 코드(Authorization Code)가 전달된다.
  5. 클라이언트는 액세스 토큰(Access Token)을 요청한다. 이전에 전달받은 인증 코드를 파라미터로 포함한다.
  6. 인증 서버는 클라이언트의 요청이 유효하다면 액세스 토큰을 반환한다.
  7. 클라이언트는 사용자의 프로필 정보를 요청한다. 이전에 반환받은 액세스 토큰을 파라미터로 포함시킨다.
  8. 인증 서버는 클라이언트의 요청이 유효하다면 유저 프로필 정보를 반환한다.
  9. 클라이언트는 JWT 토큰을 발행하고 이 토큰을 포함시켜 프론트 콜백 URI(Front Callback URI)로 리다이렉트 한다.
  10. 프론트(Front)는 JWT 토큰을 저장하고 이후 작업을 수행한다.

OAuth 동작 과정

 

  1. 사용자가 클라이언트 서비스를 이용하기 위해 요청을 보낸다.
  2. 클라이언트는 검증 서버에 액세스 토큰을 요청한다.
  3. 검증 서버는 사용자에게 인가 동의를 요청한다.
  4. 사용자는 인가 동의를 응답한다.
  5. 검증 서버는 리소스에 접근할 수 있는 액세스 토큰을 생성해 클라이언트로 전송한다.
  6. 클라이언트는 액세스 토큰을 저장한다.
  7. 클라이언트는 액세스 토큰을 가지고 리소스 서버에 요청을 보낸다.
  8. 리소스 서버는 액세스 토큰의 유효성을 파악하고 나서, 요청한 리소스를 클라이언트에 응답한다.
  9. 클라이언트는 사용자에게 서비스 이용을 응답한다.

카카오 로그인 화면에서 로그인이 완료된다면 OAuth2LoginAuthenticationProvider 클래스에서 DefaultOAuth2UserService 클래스의 loadUser 메서드를 통해 사용자 정보를 가져올 수 있다.

이 정보를 이용하여 로그인 사용자 정보를 DB에 넣을 것이다.

 

구현체 DefaultOAuth2UserService의 인터페이스인 OAuth2UserService를 커스텀해준다.

 

DB에 사용자 정보를 저장 or 업데이트를 하기 위해 코드 추가

이를 통해, 카카오 로그인을 성공하게 되면 받아온 정보를 기반으로 DB에 사용자 정보를 저장해 준다.

 

JWT 토큰 발급

이후 AuthenticationSuccessHandler를 커스텀하여 JWT 토큰을 발급해 준다.

 

추가 구현 내용

인증에 필요한 부분은 SecurityFilterChain을 거친다.

만약, 권한이 없거나 인증이 되지 않은 상태라면 해당 내용을 API로 내려줘야 하는데 기존에 구현되어 있는 코드로 인해 로그인 페이지로 자동 리다이렉트가 돼버린다.

이렇게 되면 프론트 측에서는 어떤 이유 때문에 로그인 페이지로 이동되는지 알 수도 없고, 응답을 내려주는 게 백엔드의 역할이기에 리다이렉트 시켜주는 필터를 찾아서 응답을 내려주는 필터로 커스텀해주기로 했다.

Security filter 확인

Security 디버그를 설정하여 확인을 해본 결과 ExceptionTranslationFilter에 로그인 페이지로 리다이렉트 시키는 기능이 있는 걸 확인했다.

 

handleSpringSecurityException 메서드를 보면 AuthenticationException은 인증 예외, AccessDeniedException은 권한 거부 예외와 관련된 내용이라고 짐작할 수 있다.

 

1. AccessDeniedException

 

AccessDeniedHandler

 

AccessDeniedHandlerImpl (AccessDeniedHandler의 구현체)

request.getRequestDispatcher(this.errorPage).forward(request, response); 부분에 의해

자동적으로 로그인 페이지로 리다이렉트가 된다고 판단했다.

 

RequestDispatcher란?

바인딩을 하여 데이터까지 해당 주소로 넘겨주는 기능

 

이 부분을 커스텀해보자.

ApiAccessDeniedHandler (직접 만든 클래스)

로그인 페이지로 리다이렉트 시키는 기능이 아닌, 응답을 내려주는 코드를 설정해 줬다.

 

2. AuthenticationException

 

AuthenticationEntryPoint

 

Http403ForbiddenEntryPoint (AuthenticationEntryPoint 구현체)

AuthenticationException이 발생하면 Http403ForbiddenEntryPoint에서 처리하는 걸 확인했다.

이 부분을 커스텀해서 응답으로 내려주자

 

ApiAuthenticationEntryPoint (직접 만든 클래스)

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

HTTP 상태 코드 정리  (0) 2023.12.09
Log Level  (1) 2023.12.05
커넥션 풀 VS 캐시  (1) 2023.12.05
sleep() vs wait()  (1) 2023.12.05
NginX  (1) 2023.12.05