Blog-V4 JWT 활용하기

JWT, 리팩토링,암호화
홍윤's avatar
Sep 20, 2024
Blog-V4 JWT 활용하기
 
 

암호화

notion image
notion image

1. jwt.io 홈페이지

  • 내가 만든 Jwt 토큰 인코딩 할 수 있다!
 
notion image
 
💡

HEADER(빨간색 부분)

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
결과
//JSON { "alg": "HS256", "typ": "JWT" }

payload(보라색 부분)-개인정보를 넣지 않는다.(Email,등)

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
//JSON { "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }

VERIFY SIGNATURE(하늘색 부분)

SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), your-256-bit-secret )
 

2. Blog-V4 (리팩토링) JWT 활용

1. User Package

1. UserService(로그인 기능)

public String 로그인(UserRequest.LoginDTO loginDTO) { //1. 해당 유저가 있는 조회 User user = userRepository.findByUsernameAndPassword(loginDTO.getUsername(), loginDTO.getPassword()) .orElseThrow(() -> new Exception401("인증되지 않았습니다")); //2. 조회가 되면, JWT 만들고 return 응답하기 String accessToken = JwtUtil.create(user); return accessToken; }
  • 코드설명
💡
  1. JWT 생성 (Json Web Token)
      • JwtUtil.create(user)는 사용자의 정보를 기반으로 JWT 토큰을 생성하는 메서드입니다.
      • user 객체에는 데이터베이스에서 조회된 사용자의 정보가 포함되어 있고, 이 정보를 사용하여 JWT를 생성합니다.
      • JWT는 주로 로그인 상태를 유지하거나 인증이 필요한 API 요청 시 사용됩니다. 이 토큰에는 사용자에 대한 정보(예: 아이디, 권한 등)를 안전하게 담을 수 있고, 토큰을 발급받은 후에는 클라이언트가 이를 인증서처럼 사용하게 됩니다.
  1. accessToken 반환
      • 생성된 JWT 토큰을 accessToken이라는 변수에 저장한 후, 이 값을 반환합니다.
      • return accessToken; 구문을 통해서, 클라이언트에게 토큰을 응답으로 반환합니다.
      • 클라이언트는 이 토큰을 이후의 요청에 포함시켜 서버에서 인증을 받을 수 있습니다.

정리

  • 데이터베이스에서 유저 정보를 조회한 후, 그 정보를 바탕으로 JWT 토큰을 생성합니다.
  • 생성된 JWT는 클라이언트에게 반환되며, 이후 클라이언트는 이 토큰을 인증 수단으로 사용할 수 있습니다.
 

2. UserController

@PostMapping("/login") public ResponseEntity<?> login(@Valid @RequestBody UserRequest.LoginDTO loginDTO, Errors errors) { String accessToken = userService.로그인(loginDTO); return ResponseEntity.ok() //header를 넣으면 body를 꼭 써줘야한다. Bearer 띄우고 accessToken을 넣어야한다. .header("Authorization","Bearer"+accessToken) .body(Resp.ok(null)); }
  • 코드설명
💡

1.String accessToken = userService.로그인(loginDTO)

  • userService로그인 메서드를 호출하여, 로그인 정보를 처리하고 JWT 토큰을 생성합니다.
  • 이 메서드는 사용자가 입력한 아이디와 비밀번호가 맞는지 검증한 후, 맞다면 accessToken(JWT)을 생성하여 반환합니다.

2.return ResponseEntity.ok()

  • 로그인 성공 시, HTTP 200 OK 상태 코드를 응답으로 보냅니다.
  • ResponseEntity.ok()는 HTTP 상태 코드 200을 의미합니다.

3..header("Authorization", "Bearer"+accessToken)

  • 응답 헤더에 Authorization 필드를 추가합니다.
  • Authorization 필드에 Bearer 토큰 방식으로 JWT를 포함합니다. Bearer와 토큰 사이에 공백이 필요하므로 "Bearer "+accessToken처럼 띄어쓰기가 포함되어 있습니다.
  • 클라이언트는 이 헤더를 사용하여 인증이 필요한 다른 요청을 할 때, JWT를 전달하게 됩니다.

4. .body(Resp.ok(null))

  • 응답의 본문(Body) 부분에 Resp.ok(null)를 넣습니다.
  • 이 부분에서는 별다른 데이터 없이 응답을 보내기 위해 null을 넣었지만, 필요에 따라 데이터를 포함할 수도 있습니다.
  • Resp.ok()는 일반적으로 표준화된 응답 객체를 만들어주는 헬퍼 메서드로, 성공적인 응답을 나타냅니다. null을 넣은 이유는 로그인에서 별도로 응답할 데이터가 없기 때문입니다.

정리

  • 클라이언트가 로그인 요청을 보내면 서버는 해당 로그인 정보를 검증하고, JWT 토큰을 생성하여 Authorization 헤더에 포함시켜 응답합니다.
  • 이후 클라이언트는 이 JWT를 사용하여 다른 보호된 API 요청을 할 수 있게 됩니다.
 
 

2. util Package

1. jwtUtil

public class JwtUtil { public static String create(User user){ String accessToken = JWT.create() .withSubject("바보") //유효시간 정하기 7일을 줬다 토큰을 잃어버리면 7일동안 아무것도 할 수 없이 털린다. .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7)) .withClaim("id", user.getId())// payload 자리 .withClaim("username", user.getUsername()) .sign(Algorithm.HMAC512("metacoding"));//SIGNATURE 자리 return accessToken; }
  • 코드설명
💡
이 코드는 JwtUtil 클래스에서 JWT(Json Web Token)를 생성하는 메서드입니다. 사용자의 정보를 바탕으로 JWT 토큰을 만들고, 이를 String 형태로 반환합니다. 코드를 하나씩 설명하겠습니다.

1. public static String create(User user)

  • create 메서드는 User 객체를 받아서 JWT를 생성하고 반환합니다.
  • 이 메서드는 정적(static) 메서드이므로 인스턴스 생성 없이 JwtUtil.create()로 호출할 수 있습니다.

2. String accessToken = JWT.create()

  • JWT 토큰을 생성하기 위한 시작 부분입니다. JWT.create()는 JWT 빌더 패턴을 사용하여 토큰을 생성하는 단계입니다.

3. .withSubject("바보")

  • JWT의 Subject 필드를 설정합니다. 보통 이 필드는 토큰의 목적이나 사용자와 관련된 식별자 정보를 담는데, 여기서는 문자열 "바보"로 설정되어 있습니다.
  • 이 값은 예시로 사용된 것으로 보이며, 실제 서비스에서는 사용자 아이디나 이메일 같은 유의미한 값을 넣는 것이 좋습니다.

4. .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7))

  • 토큰의 만료 시간을 설정합니다. withExpiresAt() 메서드를 사용하여 유효 기간을 지정합니다.
  • 현재 시간(System.currentTimeMillis())으로부터 7일 후를 만료 시간으로 설정하였습니다. 1000 * 60 * 60 * 24 * 7은 7일을 밀리초로 변환한 값입니다.
    • 1000: 1초를 밀리초로 변환 (1초 = 1000 밀리초)
    • 60: 1분은 60초
    • 60: 1시간은 60분
    • 24: 하루는 24시간
    • 7: 7일

5. .withClaim("id", user.getId())

  • Claim 필드를 추가합니다. JWT의 Claim은 사용자나 애플리케이션 관련 데이터를 담는 영역입니다.
  • 여기서는 id라는 Claim에 user.getId() 값을 저장하여, 사용자의 고유 ID를 JWT에 포함시킵니다.

6. .withClaim("username", user.getUsername())

  • username이라는 Claim에 user.getUsername() 값을 저장하여, 사용자의 이름을 JWT에 포함시킵니다.

7. .sign(Algorithm.HMAC512("metacoding"))

  • 토큰에 서명(SIGNATURE)을 추가합니다.
  • Algorithm.HMAC512("metacoding")은 HMAC SHA-512 알고리즘을 사용하여 JWT를 서명합니다. "metacoding"은 서명에 사용되는 비밀 키(secret key)로, 서버에서만 알고 있어야 하는 중요한 정보입니다.
  • 서명은 토큰의 변조 여부를 검증하는 역할을 하며, 클라이언트가 토큰을 임의로 수정하지 못하게 합니다.

8. return accessToken

  • 생성된 JWT 토큰을 String 형태로 반환합니다. 이 토큰은 클라이언트에게 전달되어, 이후 클라이언트가 서버에 요청을 보낼 때 인증 수단으로 사용됩니다.

정리

  • 이 코드는 User 객체의 정보를 바탕으로 JWT 토큰을 생성합니다. 토큰에는 사용자 ID와 사용자 이름이 포함되며, 만료 기간은 7일로 설정됩니다.
  • 서명에는 HMAC SHA-512 알고리즘을 사용하고, 비밀 키는 "metacoding"입니다. 생성된 토큰은 클라이언트에게 반환되어 인증 목적으로 사용됩니다.
 
public static User verify(String jwt){ DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC512("metacoding")).build().verify(jwt); int id = decodedJWT.getClaim("id").asInt(); String username = decodedJWT.getClaim("username").asString(); return User.builder() .id(id) .username(username) .build(); }
  • 코드설명
💡
이 코드는 클라이언트가 제공한 JWT(Json Web Token)를 검증하고, 그 안에 담긴 사용자 정보를 추출하여 User 객체를 반환하는 역할을 합니다. JWT의 서명을 확인하고, Claim에 담긴 정보를 바탕으로 User 객체를 재구성합니다.

1. public static User verify(String jwt)

  • 이 메서드는 JWT 토큰(jwt)을 받아서 검증하고, 그 안의 정보를 바탕으로 User 객체를 반환합니다.
  • 정적(static) 메서드이므로 클래스 인스턴스 없이도 호출할 수 있습니다.

2. DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC512("metacoding")).build().verify(jwt);

  • JWT.require(Algorithm.HMAC512("metacoding")): JWT 검증을 위한 설정을 만듭니다. HMAC512 알고리즘을 사용하여 서명을 검증하는데, 여기서 비밀 키는 "metacoding"입니다. 이 키는 토큰이 생성될 때 서명에 사용된 키와 동일해야 합니다.
  • .build(): 검증기를 빌드하여 검증 준비를 마칩니다.
  • .verify(jwt): 인자로 받은 JWT를 검증합니다. 이 과정에서 토큰의 서명, 유효기간 등이 올바른지 확인하고, 검증이 실패하면 예외가 발생합니다. 검증이 성공하면 DecodedJWT 객체를 반환합니다. 이 객체는 토큰의 정보를 담고 있습니다.

3. int id = decodedJWT.getClaim("id").asInt();

  • JWT의 Claim 중 "id" 값을 추출합니다. 토큰 생성 시에 포함했던 id Claim을 꺼내고, 이를 int 타입으로 변환합니다.
  • id는 사용자의 고유 식별자입니다.

4. String username = decodedJWT.getClaim("username").asString();

  • JWT의 Claim 중 "username" 값을 추출합니다. 마찬가지로 토큰 생성 시에 포함했던 username Claim을 꺼내고, 이를 String 타입으로 변환합니다.
  • username은 사용자의 이름 또는 ID로 사용됩니다.

5. return User.builder()

  • User 객체를 빌드하는 과정입니다.
  • User.builder()를 사용하여 빌더 패턴을 통해 객체를 생성하고, 이후 사용자 정보를 설정합니다.

6. .id(id)

  • 추출한 id 값을 User 객체의 id 필드에 설정합니다.

7. .username(username)

  • 추출한 username 값을 User 객체의 username 필드에 설정합니다.

8. .build()

  • User 객체 생성을 완료합니다. 설정한 필드 값들이 반영된 User 객체를 반환합니다.

정리

  • 이 메서드는 전달된 JWT 토큰을 검증한 후, 토큰 안에 포함된 idusername Claim을 추출합니다.
  • 추출한 정보를 바탕으로 User 객체를 생성하고 반환합니다.
  • 만약 JWT의 서명 검증이 실패하거나, 토큰이 유효하지 않은 경우 예외가 발생합니다.
4o
 

3. Jwttest

public class Jwttest { @Test public void create_test(){ User user = User.builder().id(1).username("ssar").build(); String accessToken = JwtUtil.create(user); System.out.println(accessToken); } // eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiLrsJTrs7QiLCJpZCI6MSwiZXhwIjoxNzI3NDAxMDc4fQ.1Ajxs0WTdg40m1CNloFCty1adboo5FpE7qMS0-IfICKFO10xO0u_0dn5R-Cfb9kNdramh15HxGg4kyC7cadJfw @Test public void verify_test(){ String accessToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiLrsJTrs7QiLCJpZCI6MSwiZXhwIjoxNzI3NDAyMTg5LCJ1c2VybmFtZSI6InNzYXIifQ.FyBOadpjVaXKRKw4XiCwIeWz-J7aP8gleudP-7erCkxa1L4iEEKAWG6g8-zhseEtdKcG9etpPqloJxHEyu56PA"; User user = JwtUtil.verify(accessToken); System.out.println(user.getId()); System.out.println(user.getUsername()); } }
  • 코드설명
💡

1. create_test() 메서드

이 메서드는 JwtUtil.create() 메서드를 테스트하여 JWT를 생성합니다.

설명:

  1. User 객체 생성
      • User.builder().id(1).username("ssar").build();로 ID가 1이고 사용자 이름이 "ssar"User 객체를 생성합니다.
  1. JWT 생성
      • JwtUtil.create(user)를 호출하여 user 객체의 정보를 기반으로 JWT 토큰을 생성합니다.
  1. 토큰 출력
      • System.out.println(accessToken);로 생성된 JWT 토큰을 콘솔에 출력합니다.
      • 출력된 JWT는 나중에 검증에 사용할 수 있습니다.
이 테스트는 JwtUtil.create()가 정상적으로 작동하여 JWT 토큰을 올바르게 생성하는지 확인하는 역할을 합니다.

verify_test() 메서드

이 메서드는 JwtUtil.verify() 메서드를 테스트하여 JWT를 검증하고, JWT에 포함된 사용자 정보를 제대로 추출하는지 확인합니다.

설명:

  1. JWT 토큰 준비
      • 문자열로 JWT 토큰을 하드코딩하여 accessToken 변수에 저장합니다. 이 토큰은 실제 create_test()에서 생성된 것과 비슷한 형식입니다.
  1. JWT 검증 및 사용자 정보 추출
      • JwtUtil.verify(accessToken)을 호출하여 JWT를 검증합니다. 검증에 성공하면 JWT에 포함된 사용자 정보를 추출하여 User 객체로 변환합니다.
  1. 사용자 정보 출력
      • System.out.println(user.getId());System.out.println(user.getUsername());를 통해 User 객체에서 idusername을 추출하여 출력합니다.
      • 이 정보는 JWT에 포함된 클레임(Claim)에서 온 값입니다.
이 테스트는 JwtUtil.verify() 메서드가 정상적으로 JWT를 검증하고, JWT에서 사용자 정보를 올바르게 추출하는지 확인하는 역할을 합니다.

요약

  • create_test(): User 객체 정보를 기반으로 JWT를 생성하고 이를 콘솔에 출력하여 토큰 생성 기능을 테스트합니다.
  • verify_test(): 미리 정의된 JWT를 검증하고 그 안에 포함된 사용자 ID와 사용자 이름을 추출하여 출력하는 방식으로 검증 기능을 테스트합니다.
이 두 테스트는 JWT 생성과 검증 로직이 의도한 대로 동작하는지 확인하는 데 유용합니다.
 

4. Postman 이용하기(이제부터 HTML,Mustache를 사용 안 함, 백엔드만 하기)

notion image
Share article

Uni