SEB_BE 64일차 - JWT 인증 (Authentication)

subimm_·2022년 11월 25일
0

코드스테이츠

목록 보기
64/83

💡 오늘의 학습목표

  • 토큰 기반 인증
  • JWT

📔 JWT(JSON Web Token) 개요

  • REST API를 이용한 CSR 방식의 백엔드 서버에 사용하기 적합한 JWT
  • Json 포맷으로 사용자에 대한 속성을 저장하는 웹 토큰

📖 토큰 기반 인증

  • 세션 기반 인증 = 서버(혹은 DB)에 유저 정보를 담는 방식
  • 토큰 기반 인증 = 클라이언트에게 넘김
  • 토큰 기반 인증 절차
    1. 클라이언트가 서버에 아이디/비밀번호를 담아 로그인 요청을 보냄.
    2. 아이디/비밀번호가 일치하는지 확인 후, 클라에게 보낼 암호화 된 토큰 생성
      • Access Token과 Refresh Token을 모두 생성
      • 두 종류의 토큰이 같은 정보를 담을 필요는 없다.
    3. 토큰을 클라이언트에게 전송하면, 클라는 토큰을 저장
      • 저장 위치는 Local Storage, Session Storage, Cookie 등
    4. 클라이언트가 HTTP Header(Authorization Header) 또는 쿠키에 토큰을 담아 요청 전송
      • Bearer authentication 이용
    5. 서버는 토큰을 검증하여 인증된 사용자일 경우, 요청 처리 후 응답을 보낸다.
  • 토큰 기반 인증 장점
  1. Statelessness & Scalability (무상태성 & 확장성)
  • 서버는 클라이언트에 대한 정보를 저장할 필요가 없음.
  • 토큰을 헤더에 추가함으로 인증 절차 완료
    • 여러대의 서버를 이용한 서비스라면 하나의 토큰으로 여러 서버에서 인증 가능
  1. 안정성
  • 암호화한 토큰을 사용
  • 암호화 키를 노출할 필요가 없음.
  1. 어디서나 생성 가능
  • 토큰을 생성하는 서버가 꼭 토큰을 만들지 않아도 된다.
  • 사용자의 자격 증명 정보를 직접 관리하지 않고, 깃헙, 구글 등 다른 플랫폼의 자격 증명 정보로 인증하는 것이 가능
  • 토큰 생성용 서버를 만들거나, 다른 회사에 토큰 작업을 맡기는 것 등 다양한 활용 가능
  1. 권한 부여에 용이
  • 토큰의 payload 안에 어떤 정보에 접근 가능한지 정의
  • ex) 사진과 연락처 사용권한 부여 / 사진 권한만 부여 / 연락처 권한만 부여

  • 토큰 기반 인증 단점
  1. Payload는 디코딩이 용이하다.
  • 토큰을 탈취하여 Payload를 디코딩하면 토큰 생성시 저장한 데이터를 확인할 수 있다.
    따라서 민감한 정보를 포함하지 않아야함.
  1. 토큰의 길이가 길어지면 네트워트에 부하를 줄 수 있다.

  2. 토큰은 자동으로 삭제되지 않는다.

  • 한 번 생성된 토큰은 자동삭제가 불가하므로 토큰 만료 시간을 반드시 추가해야 한다.
  • 토큰 탈취 경우를 생각하여 만료 시간을 너무 길게 설정하지 않아야 한다.

✔ 토큰 기반 자격 증명의 특징

  • 토큰에 포함된 인증된 사용자 정보는 서버 측에서 별도의 관리를 하지 않는다.
  • 생성된 토큰을 헤더에 포함시켜 요청 전송 시, 인증된 사용자인지를 증명하는 수단으로 사용
  • 토큰 내에 인증된 사용자 정보 등을 포함하므로 세션에 비해 상대적으로 많은 네트워크 트래픽 사용
  • 기본적으로 서버에서 토큰을 관리하지 않아서 보안성 측면에서 좀 더 불리하다.
  • 인증된 사용자 request의 상태를 유지할 필요가 없어 서버의 확장성 면에서 유리하고, 세션 불일치 문제 해결
  • 토큰에 포함되는 사용자 정보는 암호화 되지 않기 때문에 민감한 정보는 토큰에 포함하면 안된다.
  • 기본적으로 토큰이 만료되기 전까지는 토큰을 무효화 시킬 수 없다.
  • CSR 방식의 애플리케이션에 적합한 방식이다.

📖 세션 기반 자격 증명 방식

  • 서버 측에 인증된 사용자의 정보를 세션 형태로 세션 저장소에 저장하는 방식

✔ 세션 기반 자격 증명의 특징

  • 세션은 인증된 사용자 정보를 서버 측 세션 저장소에서 관리
  • 생성된 사용자 세션의 고유 ID인 세션 ID는 클라이언트의 쿠키에 저장되어 request 전송 시, 인증된 사용자인지를 증명하는 수단으로 사용됨.
  • 세션 ID만 클라이언트 쪽에서 사용하므로 상대적으로 적은 네트워크 트래픽을 사용
  • 서버 측에서 세션 정보를 관리하므로 보안성 측면에서 조금 더 유리하다.
  • 서버의 확장성 면에서는 세션 불일치 문제가 발생할 가능성이 높다.
  • 세션 데이터가 많아질수록 서버의 부담이 가중될 수 있다.
  • SSR 방식의 애플리케이션에 적합한 방식.
  • 세션의 단점 보완을 위해 다른 기술 사용
    • 세션 저장소 (redis)

📖 JWT

  • JSON 포맷의 토큰 정보를 인코딩 후, 인코딩 된 토큰 정보를 Secret Key로 서명한 메시지를 Web Token으로써 인증 과정에 사용
  • JWT 공식 사이트 : https://jwt.io/
  • JWT의 종류
  1. 액세스 토큰 (Access Token)
  2. 리프레시 토큰 (Refresh Token)
  • Access Token은 보호된 정보들에 접근할 수 있는 권한부여에 사용됨.
  • 권한을 부여받는 데엔 Access Token만 있으면 되지만, 짧은 유효기간을 주어 탈취되어도 오래 사용할 수 없도록 한다.
  • Access Token의 유효기간이 만료되면 Refresh Token을 사용하여 새로운 Access Token을 발급 받는다.
    • 리프레시토큰이 탈취되는 것을 방지 : redis 사용하여 토큰을 DB에 키값쌍으로 저장 하여 대조하는 방법도 있음.
  • JWT 구조
  • Heder
    • 어떤 종류의 토큰인지 (지금 경우엔 JWT)
    • 어떤 알고리즘으로 Sign할지 정의
      JSON 객체를 base64 방식으로 인코딩하면 JWT의 첫 부분이 생성
  • Payload ( 민감한 정보는 담지 않는 것이 좋다.)
    • 서버에서 활용할 수 있는 유저의 정보
    • 어떤 정보에 접근 가능한지에 대한 권한
    • 기타 필요한 정보
  • Signature
    • 원하는 비밀키와 Header에서 지정한 알고리즘을 사용하여 Header와 Payload에 대해서 단방향 암호화를 수행
      • 토큰의 위변조 유무를 검증 시 사용

📙 JWT 생성 및 검증 테스트

📖 JWT 생성 및 검증 테스트를 위한 프로젝트 설정

  • Spring Boot 기반의 템플릿 프로젝트 생성 및 의존 라이브러리 추가
  • JWT 생성 및 검증 테스트를 수행하기 위해 필요한 라이브러리
    여기서는 jjwt0.11.5 버전을 사용함.

📖 JWT 생성

  • ✔ JWT 생성 기능 구현
  • (1) 메서드는 Plain Text 형태인 Secret Key의 byte[]를 Base64 형식의 문자열로 인코딩 해준다.
    • jjwt가 버전업 되면서 Plain Text 자체를 Secret Key로 사용하는 것을 권장하지 않고 있다.

  • (2) 메서드는 인증된 사용자에게 JWT를 최초로 발급해주기 위한 JWT 생성 메서드이다.
    • (2-1) Base64 형식 Secret Key 문자열을 이용해 Key(java.security.Key) 객체를 얻는다.
  • (2-2) setClaims() JWT에 포함 시킬 Custom Claims를 추가한다. (주로 인증된 사용자와 관련된 정보 추가)
  • (2-3) setSubject() JWT에 대한 제목 추가
  • (2-4) setIssuedAt() JWT 발행 일자를 설정, 파라미터 타입은 java.util.Date 타입이다.
  • (2-5) setExpiration() JWT의 만료일시 지정. 파라미터는 역시 Date 타입
  • (2-6) signWith() 에 서명을 위한 Key 객체를 설정
  • (2-7) compact() 를 통해 JWT를 생성하고 직렬화한다.

  • (3) 메서드는 Access Token이 만료되었을 경우, 새로 생성할 수 있게 해주는 Refresh Token을 생성하는 메서드이다. (별도의 Custom Claims는 추가할 필요 없다.)

  • (4) 메서드는 JWT의 서명에 사용할 Secret Key를 생성해준다.
    • (4-1) Decoders.BASE64.decode() 메서드는 Base64 형식으로 인코딩 된 시크릿키를 디코딩 한 후, byte[]를 반환.
    • (4-2) Keys.hmacShaKeyFor() 는 key byte array를 기반으로 적절한 HMAC 알고리즘을 적용한 Key 객체를 생성한다.
      (jjwt 최신버전에서는 내부적으로 적절한 HMAC알고리즘을 지정해준다.)

✔ JWT 생성 기능 테스트

  • (1) 에서 테스트에 사용할 Secret Key를 Base64 형식으로 인코딩 한 후, 인코딩 된 시크릿 키를 각 테스트 케이스에서 사용
  • (2) Plain Text인 시크릿키가 Base64 형식으로 인코딩이 정상적으로 수행이 되는지 테스트
    • Base64 형식으로 인코딩 된 시크릿키를 디코딩한 값이 원본과 일치하는지 테스트
  • (3) JwtTokenizer가 Access Token을 정상적으로 생성하는지 테스트
  • (4) Refresh Token을 정상적으로 생성하는지 테스트

📖 JWT 검증

✔ JWT 검증 기능 구현

  • JwtTokenizer 클래스에 JWT 검증을 위한 메서드 추가
  • jjwt에서는 JWT를 생성할 때 서명에 사용된 시크릿키를 이용해 내부적으로 Signature를 검증 한 후, 검증에 성공하면 JWT를 파싱해서 Claims를 얻을 수 있다.
    • (1) 의 setSigningKey() 메서드로 서명에 사용된 시크릿키를 설정
    • (2) 의 parseClaimsJws() 메서드로 JWT를 파싱해서 Claims를 얻는다.
      • verifySignature() 메서드는 Signature를 검증하는 용도로 Claims를 리턴할 필요는 없다.
      • 파라미터로 사용한 jws는 Signature가 포함된 JWT라는 의미

✔ JWT 검증 기능 테스트

  • (1) 에서 구현한 JwtTokenizer의 verifySignature() 메서드가 Signature를 검증하는지 테스트
    • 생성된 JWT를 verifySignature()로 전달해서 Exception이 발생하지 않는다면 검증이 잘 수행된 것으로 본다.
  • (2) JWT 생성시 지정한 만료일시가 지나면 JWT가 정말 만료되는지 테스트
    • 생성되는 JWT의 만료 주기를 짧게 준 후에 첫번째 Signature 검증을 수행하고, 만료일시가 지나도록 지연시간을 준 뒤, 두 번째 검증을 수행했을 경우ExpiredJwtException이 발생하면 정상적으로 JWT가 만료

profile
코린이의 공부 일지

0개의 댓글