본문 바로가기
내일배움캠프(Sparta)/Spring

[Spring] JWT / 패스워드 암호화

by mmm- 2023. 11. 8.

1) JWT (JSON Web Token)

: JSON 포맷을 이용해 사용자에 대한 속성을 저장하는 Claim 기반의 Web Token

  • 토큰의 한 종류
  • 일반적으로 쿠키 저장소를 사용해 JWT 전달

 

JWT 사용 이유

1. 서버가 1대인 경우

  • Session1이 모든 Client의 로그인 정보를 소유하고 있음.

 

2. 서버가 2대 이상인 경우

  • 대용량 트래픽 처리를 하기 위해서는 서버가 두 대 이상 운영이 필요함.
  • 중간에서 클라이언트의 요청을 밸런스있게 나눠주는 중간 역할을 하는 Load Balancer가 존재해
    Session마다 다른 Client 로그인 정보를 가지고 있을 수 있음.
    • ex
      • Session1 : Client1, Client2, Client3
      • Session2 : Client4
      • Session3 : Client5, Client6
  • 만약 Client 1의 로그인 정보를 가지고 있지 않은 Server2나 Server3에 API 요청을 하게되면 문제가 발생하지 않을까?
    • 해결 방법
      1. Sticky Session: Client마다 요청 Server 고정
      2. 세션 저장소 생성하여 모든 세션을 저장
        (아래의 세션 저장소 생성 참고)

 

세션 저장소 생성

 

  • 외부의 Session storage 가 모든 Client의 로그인 정보를 소유하고 있다면 모든 서버에서 어떠한 클라이언트가 들어오든지 Session stroage에 물어보면 되기 때문에 모두 처리 가능.

 

JWT 사용

  • 로그인 정보를 Server에 저장하는게 아닌, Client에 로그인 정보를 JWT로 암호화하여 저장한 후 모든 서버에서 토큰이 들어왔을 때 JWT 통해 인증/인가만 하면 됨.
  • 모든 서버에서 동일한 비밀키를 가짐.
  • Secret Key를 통한 암호화 / 위조 검증 (복호화 시)

 

  • JWT 장/단점
    • 장점
      • 동시 접속자가 많을 때 서버 측 부하를 낮춰줌.
        • DB같은 다른 storage랑 연결을 해서 체크하는 게 아니라, 동일한 값만 가지고 있으면 서버 자체에서 검증이 가능하기 때문에 
      • Client, Server가 다른 도메인을 사용할 때
        • ex) 카카오 OAuth2 로그인 시 JWT Token 사용
    • 단점
      • 구현의 복잡도 증가
      • JWT에 담는 내용이 커질수록 네트워크 비용 증가 (클라이언트 → 서버)
        • JWT안에 많은 정보가 담겨있으면 JWT의 길이도 길어지게 되고, 이것이 HTTP 프로토콜에 담겨서 들어오는데, HTTP 프로토콜이 무겁고 용량이 클수록 네트워크 비용이 증가 함.
      • 기 생성된 JWT를 일부만 만료시킬 방법이 없음
      • 비밀키 유추시 JWT 조작 가능.
        • 그러므로 JWT안에는 비밀번호 같이 민감한 데이터는 넣으면 안됨.
  • JWT 사용 흐름
    1. Client가 username, password로 로그인 성공 시
      1. ) 서버에서 "로그인 정보" → JWT로 암호화 (비밀키 사용)
      2. ) 서버에서 직접 쿠키를 생성해 JWT를 담아 Client 응답에 전달
      3. ) 브라우저 쿠키 저장소에 자동으로 JWT가 저장됨
    2. Client에서 JWT통한 인증 방법
      1. ) 서버에서 API 요청시마다 쿠키에 포함된 JWT를 찾아 사용
      2. ) Server
        1. Client가 전달한 JWT 위조 여부 검증 (비밀키 사용)
        2. JWT 유효기간이 지나지 않았는지 검증
        3. 검증 성공시
          •  JWT 에서 사용자 정보를 가져와 확인
            ex; GET/api/products : JWT 보낸 사용자의 관심상품 목록 조회
  • JWT 구조
    • JWT는 누구나 복호화 가능하지만 비밀키가 없으면 JWT 수정 불가
    • https://jwt.io/
    • Header, Payload, Signature 부분으로 구성
      • Payload에는 실제 유저의 정보가, Header와 Verify Signcature 부분은 암호화와 관련된 정보 양식이 들어있음.

Util 클래스

: 특정 매개 변수에 대한 작업을 수행하는 메서드들이 존재하는 클래스

  • 다른 객체에 의존하지 않고 하나의 모듈로서 동작하는 클래스 
  • 어떠한 기능들을 가진 메서드들의 한 묶음(한 모듈)
    ex; JWTUntil - JWT관련된 기능들을 가진 Class

@Enumerated

: EnumType을 DB 컬럼에 저장할 때 사용하는 annotation

  • EnumType.STRING 옵션을 사용하면 Enum의 이름을 DB에 그대로 저장
    • ex; USER(Authority.USER) → USER

 


패스워드 암호화

  • 정보통신망법, 개인정보보호법 에 의해 비밀번호 암호화는 의무!


2) 패스워드 암호화

해커가 DB에서 패스워드 정보를 갈취하더라도 실제 암호를 알 수 없음. 그래서 복호화가 불가능한 단방향 암호 알고리즘 사용 필요

 

양방향 암호 알고리즘

  • 암호화: 평문 → (암호화 알고리즘) → 암호문
  • 복호화: 암호문  → (암호화 알고리즘) → 평문

단방향 암호 알고리즘

  • 암호화: 평문 → (암호화 알고리즘) → 암호문
  • 복호화: 불가
그럼 사용자는 로그인할 때 암호화된 패스워드를 기억해야 할까?

String Security라는 프레임워크에서 passwordEncoder가 제공하는 matches를 이용해 사용자가 입력한 비밀번호를 DB에 저장되어있는 암호화된 비밀번호와 비교해 일치여부를 확인해줌.

matches 메서드는 두 개의 인자를 받아 비교 후 일치여부 확인
➡️ 첫 번째 인자 - 원본 비밀번호 / 두 번째 인자 - 암호화된 비밀번호