Spring Security + JWT 로그인 회원가입 API 구현 (1) - 인증,인가, JWT이란

이동엽·2023년 8월 18일
0

스프링 시큐리티와 같은 보안 기술을 이해 하는게 쉽지않다... 이번에 jwt를 이용해 회원가입 및 로그인 구현을 하는데 모르는 상태에서 남의 코드를 쓸려고 하니 구현을 못해 이번에 공부하면서 익힐려고 합니다.

인증(Authentication)과 권한(Authorization)

인증은 보호된 리소스에 접근하는 대상, 즉 사용자에게 적절한 접근 권한이 있는지 확인하는 일련의 과정.
이때 보호된 리소스에 접근하는 대상(사용자)를 접근 주체(Principal)이라고 한다.
권한은 인증 절차가 끝난 접근 주체가 보호된 리소스에 접근 가능한지를 결정하는 것을 의미 합니다. 이때 권한을 부여하는 작업을 인가(Authorize)이라고 한다.

쉽게 이해하자면 인증은 아이디,비밀번호로 로그인 하는 과정이며, 권한이 필요한 리소스에 접근 하기 위해선 당연히 이러한 인증 과정을 거쳐야 한다.
스프링 시큐리티는 이런 인증 매커니즘을 간단하게 만들 수 있도록 다양한 옵션들을 제공하고 있다.
또한 웹 요청이나 메소드 호출, 도메인 인스턴스에 대한 접근 등 상당히 깊은 수준의 권한 부여를 제공하고 있다.

스프링 시큐리티의 구조

스프링 시큐리티는 주로 서블릿 필터와 이들로 구성된 필터체인을 사용하고 있다. 서블릿 필터와 관련된 설명은 이전 포스팅을 참조 부탁드린다. 그렇다면 실제 로그인 시에 스프링 시큐리티의 동작 플로우를 바탕으로 인증과 스프링 시큐리티 아키텍처를 보자면.


개략적인 인증 과정의 흐름은
1. 사용자가 로그인 정보와 함께 인증 요청(Http Request)
2. AuthemticationFilter가 이 요청을 가로챈다. 이때 가로챈 정보를 통해 UsernamePasswordAuthenticationToken이라는 인증용 객체를 생성한다.
3. Authentication Manager의 구현체인 ProviderManager에게 UsernamePassword AuthenticationToken 객체를 전달한다.
4. 다시 AuthenticationProvider에 UsernamePasswordAuthenticationToken 객체를 전달한다.
5. 실제 데이터베이스에서 사용자 인증정보를 가져오는 UsernamePasswordAuthenticationToken 객체를 전달한다.
6. 넘겨받은 사용자 정보를 통해 DB에서 찾은 사용자 정보인 UserDetails 객체를 만든다. 이때 UserDetails는 인증용 객체와 도메인용 객체를 분리하지 않고 인증용 객체에 상속해 사용한다.
7. AuthenticationProvider는 UserDetails를 넘겨받고 사용자 정보를 비교한다.
8. 인증이 완료되면 권한 등의 사용자 정보를 담은 Authentication 객체를 반환한다.
9. 다시 최초의 AuthenticationFilter에 Authentication 객체가 반환된다.
10. Authentication 객체를 SecurityContext에 저장한다.

최종적으로 SecurityContextHolder는 세션 영역에 있는 SecurityContext에 Authentication 객체를 저장한다. 세션에 사용자 정보를 저장한다는 것은 스프링 시큐리티가 전통적인 세션-쿠키 기반의 인증 방식을 사용한다는 것이다.

SpringSecurity Filter

실제로 스프링 시큐리티는 훨씬 다양한 필터 체인을 사용해 다양한 커스터마이징을 할 수 있도록 돕는다. 대략 적인 내용은

  • SecurityContextPersistentFilter : SecurityContextRepository에서 SecurityContext를 가져와서 SecurityContextHolder에 주입하거나 반대로 저장하는 역할을 한다.
  • LogoutFilter : logout 요청을 감시하며, 요청시 인증 주체(Principal)를 로그아웃 시킨다.
  • UsernamePasswordAuthenticationFilter : login 요청을 감시하며, 인증 과정을 진행한다.
  • DefaultLoginPageGenerationFilter : 사용자가 별도의 로그인 페이지를 구현하지 않은 경우, 스프링에서 기본적으로 설정한 로그인 페이지로 넘어가게 시킨다.
  • BasicAuthenticationFilter : HTTP 요청의 (BASIC)인증 헤더를 처리하여 결과를 SecurityContextHolder에 저장한다.
  • RememberMeAuthenticationFilter : SecurityContext에 인증(Authentication) 객체가 있는지 확인하고 RememberMeServices를 구현한 객체 요청이 있을 경우, RememberMe를 인증 토큰으로 컨텍스트에 주입한다.
  • AnonymousAuthenticationFilter : 이 필터가 호출되는 시점까지 사용자 정보가 인증되지 않았다면 익명 사용자로 취급한다.
  • SessionManagementFilter : 요청이 시작된 이후 인증된 사용자인지 확인하고, 인증된 사용자일 경우 SessionAuthenticationStrategy를 호출하여 세션 고정 보호 매커니즘을 활성화 하거나 여러 동시 로그인을 확인하는 것과 같은 세션 관련 활동을 수행한다.
  • ExceptionTranslationFilter : 필터체인 내에서 발생되는 모든 예외를 처리한다.
  • FilterSecurityInterceptor : AccessDecisionManager로 권한부여처리를 위임하고 HTTP 리소스의 보안 처리를 수행한다.

JWT

Json Web Token의 약자.
JSON 객체를 통해 안전하게 정보를 전송할 수 있는 웹표준(RFC7519)이다. JWT는 '.'을 구분자로 세 부분으로 구분되어 있는 문자열로 이루어져 있습니다. 각각 헤더는 토큰 타입과 해싱 알고리즘을 저장하고, 내용은 실제로 전달할 정보, 서명에는 위변조를 방지하기위한 값이 들어가게 됩니다.


구성 요소는 Header,Payload,Signature 세가지로 구성된다.

  1. Header

    Header는 토큰의 타입이나, 전자서명 시 어떤 알고리즘이 사용됐는지 저장한다.
    예제 : HS512 알고리즘

  2. Payload

    Payload에는 보통 Claim이라는 토큰에서 사용할 정보들이 담겨있다.
    위에 있는 사진엔 key-value 형식으로 이루어진 하나의 쌍들이 모두 Claim이다.
    인증시에 토큰에서 실제로 사용될 정보를 의미한다.
    여러 Claim들을 JWT 토큰 생성 시에 개발자가 어떤 Claim을 넣을지 정한 후 마음대로 넣을 수 있다.

JWT의 표준 스펙에는 7가지의 Claim이 정의되어 있다.
근데 꼭 7가지를 모두 포함해야 하는건 아니다.

1. iss(Issuer) : 토큰 발급자
2. sub(Subjec) : 토큰 제목 - 토큰에서 사용자에 대한 식별값이 된다.
3. aud(Audience) : 토큰 대상자
4. exp(Expiration Time) : 토큰 만료 시간
5. nbf(Not Before) : 토큰 활성 날짜 (이 날짜 이전의 토큰은 활성화 되지 않음을 보장)
6. iat(Issued At) : 토큰 발급 시간
7. jti(JWT Id) : JWT 토큰 식별자 (issuer가 여러 명일 때 구분하기 위한 값)

이런 식으로 표준 스펙들이 있는데 필요하면 개발자가 추가로 작성해도된다.
구현할때 토큰에서 사용자의 email를 추출하기위해 사용자 정의 claim인 'email'를 별도로 추가 하면된다.

주의 해야할점은 Payload에는 암호화가 되어 있지 않기 때문에, 민감한 정보를 담지 않아야한다.
누구나 JWT Decoding을 통해 Payload의 정보를 볼수 있기 때문에 민감한 정보를 안넣고 식별하는 정보만 담아야한다.

동작 과정

API 서버는 로그인 요청이 완료되면 클라이언트에게 회원을 구분할 수 있는 정보를 담은 JWT를 생성해 전달합니다. 그러고 클라이언트는 이 JWT를 헤더에 담아서 요청을 하게 됩니다. 권한이 필요한 요청이 있을때 마다 API 서버는 헤더에 담긴 JWT 값을 확인하고 권한이 있는 사용자인지 확인하고 리소스를 제공하게 된다.

이렇게 세션-쿠키 기반의 로그인이 아닌 JWT 같은 토큰 기반의 로그인을 하면 다중 서버 환경에서도 로그인을 유지할 수 있고, 한번 로그인으로 유저 정보를 공유하는 여러 도메인에서 사용할 수 있는 장점이 있다.

profile
씨앗

1개의 댓글

comment-user-thumbnail
2023년 8월 18일

개발자로서 성장하는 데 큰 도움이 된 글이었습니다. 감사합니다.

답글 달기