JWT V2

암호화 (with Test)
SHIN's avatar
Sep 20, 2024
JWT V2
 

1. JWT 구성

notion image
 
notion image
 
💡
  1. Header
      • 토큰이 어떤 타입인지 (JWT)와 서명에 사용할 알고리즘(HS256)을 정의
      • 위의 사진에서는 “alg” : “HS2556”, 즉 HMAC-SHA256 알고리즘을 사용
  1. Payload
      • 토큰에 담길 실제 데이터 포함
      • 보통 유저에 대한 정보, 만료 시간, 발행자등을 포함
      • 이 정보는 누구나 읽을 수 있지만 서명을 통해 위조 여부 검증 가능
  1. Signature
      • 토큰의 위조 방지 부분
      • 서버는 서명을 통해 토큰이 변조되지 않았는지 확인
      • 서명은 header와 payload를 연결 후, secret key로 서명하여 생성
      • 위의 사진에서는 HMAC-SHA256 알고리즘을 통해 생성
 
1-1. Hash, HS256, HMAC 연관성
💡
  • Hash
    • 입력 데이터를 고정된 크기의 고유한 출력 값으로 변환하는 함수
    • Ex) SHA256 = 임의의 길이의 데이터를 256bit의 고정된 크기의 hash 값으로 변환하는 hash 알고리즘
    • 이 변환은 단방향으로 원래의 데이터를 hash 값으로부터 되돌리는 것은 불가능
  • HS256
    • HMAC을 사용한 hash 알고리즘인 “HMAC-SHA256”의 줄임말
    • 이는 JWT에서 서명 생성 시 사용하는 알고리즘
    • HS256 = SHA256 hash 함수와 secret key를 결합한 HMAC 알고리즘을 통해 서명 생성
  • HMAC (hash 기반 메시지 인증 코드)
    • hash함수와 secret key를 사용하여 메시지의 무결성 확인 및 메시지가 변조되지 않았는지 확인하는 방법
    • 즉 단순한 hash 함수에 secret key를 추가해서 더 안전한 인증 방식 제공
 
정리
💡
  • JWT는 header, payload, signature 로 구성되며, 이를 통해 데이터가 변조되지 않았는지 확인 가능
  • signature 부분에서 HS256 알고리즘은 HMAC을 사용해 SHA256 hash 함수로 생성
  • hash는 데이터를 고정된 길이의 값으로 변환하며 HMAC은 hash에 secret key를 추가해서 더 안전한 signature 제공
 
HMAC 참조 사이트
 

 

2. 암호화

 
단방향
💡
  • 복호화 안됨 → 잠금만 가능 (Encoding만 가능)
  • 암호화 했을 때 풀 수 없을 때 사용 → 서명, 위조 검증
 
양방향
💡
  • 암호화 & 복호화 가능 → Encoding, Decoding 가능
  • 대칭키 (열쇠 1개) : 생성과 검증을 한 명이 할 때.
  • 공개키 (열쇠가 쌍으로 2개) : 생성과 검증을 다른 사람이 할 때.
 
Base64 (양방향 = 복호화 가능)
💡
  • 아스키 코드 : 8bit → 2의 8승 → 경우의 수 : 256
  • Base64 : 6bit → 2의 6승 → 경우의 수 : 64
  • 파일, 사진들을 문자화해서 전송하기 위해서 사용.
  • 왜 사용? → JSON { } 에 심어서 함께 전송가능 = 통신 체계 심플
 

 

1. Controller (login 메소드)

@PostMapping("/login") public ResponseEntity<?> login(@RequestBody UserRequest.LoginDTO loginDTO, Errors errors) { String acessToken = userService.로그인(loginDTO); return ResponseEntity.ok() .header("Ahthorization", "Bearer " + acessToken) // header를 넣으면 body도 넣어야 함 -> 문법 .body(Resp.ok(null)); // "Bearer " = access token (protocol) }
  • 유저가 로그인 요청 시 loginDTO 객체에 유저이름과 비밀번호 정보가 들어오고, 이를 userService.로그인() 메소드를 호출해서 검증
  • 검증 성공 시 JWT Access Token 발급, 이를 HTTP 응답 헤더에 Authorization: Bearer <토큰> 형식으로 담아서 반환
  • 웅답 본문(body)에는 추가적인 데이터가 없고, Resp.ok(null)로 응답
 

2. Service (로그인 메소드)

public String 로그인(UserRequest.LoginDTO loginDTO) { // 1. 해당 유저 존재 유무 조회 User user = userRepository.findByUsernameAndPassword(loginDTO.getUsername(), loginDTO.getPassword()) .orElseThrow(() -> new Exception401("인증되지 않았습니다")); // 2. 조회되면 JWT 만들고 응답 String accessToken = JwtUtil.create(user); return accessToken; }
  • DB에서 loginDTO로 받은 유저이름과 비밀번호를 조회, 해당 유저 존재 시 JWT 생성
  • JwtUtil.create(user) 는 JWT를 생성하는 메소드, 해당 JWT는 추후 클라이언트에게 응답으로 제공
 

3. JwtUtil (create, verify 메소드)

public static String create(User user){ String accessToken = JWT.create() // 이 정보로 단방향 hash .withSubject("TEST") .withExpiresAt(new Date(System.currentTimeMillis() + 1000*60*60*24*7)) // 토큰 만료 시간 .withClaim("id", user.getId()) // payload (keymap) .withClaim("username", user.getUsername()) .sign(Algorithm.HMAC512("shin")); // secret 키 같은 개념 return accessToken; } public static User verify(String jwt){ DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC512("shin")).build().verify(jwt); int id = decodedJWT.getClaim("id").asInt(); String username = decodedJWT.getClaim("username").asString(); return User.builder() .id(id) .username(username) .build(); }
  • create(User user)
    • JWT 생성 메소드
    • 유저의 ID, username 정보를 담고, 만료 기간은 현재 시점에서 7일로 설정
    • 최종적으로 HMAC512 알고리즘을 사용해 서명(sign)하여 토큰 반환
  • verify(String jwt)
    • JWT 검증 메소드
    • 서명된 토큰 검증, 토큰에서 idusername 클레임을 추출해서 새로운 User 객체로 반환
 
 

4. JwtUtilTest

 
4-1. JWT 생성 Test
@Test public void create_test() { User user = User.builder().id(1).username("shin").build(); String accessToken = JwtUtil.create(user); System.out.println(accessToken); } }
notion image
  • 유저 객체(User)를 만들어 JwtUtil.create() 메소드 호출, 결과로 반환된 JWT 토큰 출력
 
 
4-2. JWT 검증 Test
@Test public void verify_test() { String accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiLslYjrhZUiLCJpZCI6MSwiZXhwIjoxNzI3NDAyMDg5LCJ1c2VybmFtZSI6InNoaW4ifQ.dnmveRXQrp0dU99Y9NQtswPTsy11hNuCw5CvcY47d9ATgLj2aLo0wNviCjNJh_Nik7ISZN-9XgvHXrb1r7OS3Q"; User user = JwtUtil.verify(accessToken); System.out.println(user.getId()); System.out.println(user.getUsername()); }
notion image
  • 미리 생성된 토큰을 varify 메소드에 전달, 토큰에서 추출한 유저 정보(id, username)가 정확히 복원되는지 확인
 
Share article

SHIN