๐Ÿ‘ค JWTย ํ† ํฐ, ์ธ์ฆ ์ธ๊ฐ€

jijiยท2023๋…„ 11์›” 23์ผ
0

Spring Boot Project ๐ŸŒฑ

๋ชฉ๋ก ๋ณด๊ธฐ
15/16
post-thumbnail

๐Ÿค” JWT๋ž€?

(Json Web Token)

  • ์ผ๋ฐ˜์ ์œผ๋กœ ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ํ†ต์‹  ์‹œ ๊ถŒํ•œ ์ธ๊ฐ€(Authorization)์„ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ํ† ํฐ์ด๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๊ฐ„์˜ ์ •๋ณด๋ฅผ JSON ๊ฐ์ฒด๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ „์†กํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ
  • JWT๋Š” ์„œ๋ช…๋œ ํ† ํฐ์ด๋‹ค!

๋จผ์ € ํ† ํฐ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž!

Token์ด๋ž€?

์ด ์ž…์žฅ๊ถŒ์„ ํ†ตํ•ด์„œ ์ ‘๊ทผ์ด ํ—ˆ์šฉ๋œ ์„œ๋ฒ„์˜ ์ž์›์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

๐Ÿ’ก access token : ๋กœ๊ทธ์ธ ์ ‘๊ทผ ์นด๋“œํ‚ค
ย ย ย ย ย refresh token : ์นด๋“œํ‚ค ์žฌ๋ฐœ๊ธ‰


  1. Access Token
  • ํŠน์ • ์ž์›(๋ณดํ˜ธ๋œ ์ •๋ณด)์— ์ ‘๊ทผํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์‹ค์ œ๋กœ ๊ถŒํ•œ์„ ์–ป๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋Š” ํ† ํฐ์ด๋‹ค.
    • ํ† ํฐ์ด ํƒˆ์ทจ๋œ๋‹ค๋ฉด ์œ„ํ—˜.(๋งŒ๋ฃŒ๊ธฐ๊ฐ„์„ ์งง๊ฒŒ ์ง€์ •, ์ผ๋ฐ˜์ ์œผ๋กœ ๋ช‡๋ถ„~๋ช‡์‹œ๊ฐ„)
  • ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” access token์„ ํ•ด์„ํ•  ์ˆ˜ ์—†์–ด์•ผํ•˜๊ณ , ์„œ๋ฒ„์—์„œ ํ•ด์„ํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•œ๋‹ค. (ํƒˆ์ทจ๋  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์œผ๋ฏ€๋กœ)
  • JWTํ˜•์‹ ๋˜๋Š” UUID๋“ฑ๋“ฑ ์›ํ•˜๋Š” ํ˜•ํƒœ๋กœ ๋ฐœ๊ธ‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ์‹œ: ๋กœ๊ทธ์ธ -> Access token ๋ฐœ๊ธ‰ ๋ฐ›์Œ -> Access token์„ ํ—ค๋”์— ์ถ”๊ฐ€ํ•œ ํ›„ ์„œ๋ฒ„์— api์š”์ฒญ -> ์„œ๋ฒ„๊ฐ€ Access token์„ validation -> validation์— ํ†ต๊ณผํ–ˆ๋‹ค๋ฉด ์š”์ฒญ์— ๋งž๊ฒŒ ์‘๋‹ต

  1. Refresh Token
  • Access token์€ ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„์„ ๊ฐ–๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ๋ฃŒ ๊ธฐ๊ฐ„์ด ์ง€๋‚˜๋„ ์„œ๋ฒ„ ์ž์›์— ์ ‘๊ทผํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, Access token์„ ๊ฐฑ์‹ ํ•ด์•ผํ•œ๋‹ค. ์ด ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํ† ํฐ์ด Refresh token์ด๋‹ค.
  • ์•ก์„ธ์Šค ํ† ํฐ์˜ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด ์•ก์„ธ์Šค ํ† ํฐ์„ ๋‹ค์‹œ ๋ฐœ๊ธ‰๋ฐ›๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋ณด์•ˆ ์ƒ ์•ก์„ธ์Šค ํ† ํฐ์„ ์ €์žฅํ•˜๊ฑฐ๋‚˜ ์ „์†กํ•˜๋Š” ๋Œ€์‹ ์— ์‚ฌ์šฉ
  • ์ผ๋ฐ˜์ ์œผ๋กœ ๋ช‡์ผ~๋ช‡์ฃผ

๊ธฐ๋ณธ์ ์œผ๋กœ Access Token์€ ์™ธ๋ถ€ ์œ ์ถœ ๋ฌธ์ œ๋กœ ์ธํ•ด ์œ ํšจ๊ธฐ๊ฐ„์„ ์งง๊ฒŒ ์„ค์ •ํ•˜๋Š”๋ฐ, ์ •์ƒ์ ์ธ ํด๋ผ์ด์–ธํŠธ๋Š” ์œ ํšจ๊ธฐ๊ฐ„์ด ๋๋‚œ Access Token์— ๋Œ€ํ•ด Refresh Token์„ ์‚ฌ์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด Access Token์„ ๋ฐœ๊ธ‰๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

๋งŒ์•ฝ Refresh Token์ด ์œ ์ถœ๋˜์–ด์„œ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด Access Token์„ ๋ฐœ๊ธ‰๋ฐ›์•˜๋‹ค๋ฉด? Access Token์˜ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์„œ๋ฒ„์ธก์—์„œ๋Š” ๋‘ ํ† ํฐ์„ ๋ชจ๋‘ ํ๊ธฐ์‹œ์ผœ์•ผ ํ•œ๋‹ค.

  1. Id Token
  • ํด๋ผ์ด์–ธํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋ฅผ ์‹๋ณ„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ
  • ์ธ์ฆ ๋ฐ›์€ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ํฌํ•จ
  • ๋ฏผ๊ฐํ•œ ์ •๋ณด๋Š” ์ ˆ๋Œ€ Id ํ† ํฐ์— ํฌํ•จ์‹œํ‚ค๋ฉด ์•ˆ๋œ๋‹ค.
  • ํ† ํฐ์„ ๋ฐœ๊ธ‰๋ฐ›์€ ์‚ฌ์šฉ์ž์ธก์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ํ† ํฐ์ด๋‹ค.

JWT์˜ ์žฅ์ 

  • ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ„์˜ ํ†ต์‹ ์„ ๊ฐ„๋‹จํ•˜๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ค€๋‹ค.
  • ์ •๋ณด๋ฅผ ํ† ํฐ ์ž์ฒด์— ์ €์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— DB์— ์กฐํšŒํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. (๋น ๋ฅธ ์ธ์ฆ)
  • ํ‘œ์ค€ํ™” โ†’ ์—ฌ๋Ÿฌ ํ”Œ๋žซํผ์— ์ง€์›๋œ๋‹ค.

โš ๏ธ ๋ฐœ๊ธ‰ ํ›„ ์ˆ˜์ •์ด ๋ถˆ๊ฐ€๋Šฅ, ํ† ํฐ์„ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณด๊ด€ํ•ด์•ผ ํ•˜๊ณ , ํ•„์š”ํ•œ ์ •๋ณด๋งŒ ํฌํ•จํ•ด์•ผํ•œ๋‹ค!


๐Ÿค– ์ž‘๋™ ๋ฐฉ์‹

1) ์ธ์ฆ(Authentication)

๐Ÿ‘‰๐Ÿป ๋กœ๊ทธ์ธ : ์‚ฌ์šฉ์ž๊ฐ€ ID/PW๋ฅผ ํ†ตํ•ด ๋กœ๊ทธ์ธ์„ ์‹œ๋„

  • ์ธ์ฆ ์ฒ˜๋ฆฌ : ์„œ๋ฒ„๋Š” ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ํ™•์ธ, ์œ ํšจํ•  ๊ฒฝ์šฐ secret key๋ฅผ ํ†ตํ•ด Access token์„ ๋ฐœ๊ธ‰ํ•œ๋‹ค.
  • JWT ๋ฐœ๊ธ‰ : ์‚ฌ์šฉ์ž ์ •๋ณด์™€ ํ•จ๊ป˜ ์„œ๋ช…๋œ ํ† ํฐ์ด ์ƒ์„ฑ(ํ† ํฐ์„ ํด๋ผ์ด์–ธํŠธ์— ์ „๋‹ฌ)

2) ์ธ๊ฐ€(Authorization)

๐Ÿ‘‰๐Ÿป ๋ณดํ˜ธ๋œ ์—”๋“œํฌ์ธํŠธ : API ์—”ํŠธํฌ์ธํŠธ์— ์š”์ฒญ์„ ๋ณด๋‚ผ ๋•Œ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ Authorization header(or ๋งค๊ฐœ๋ณ€์ˆ˜..)์— Access token์„ ๋‹ด์•„์„œ ๋ณด๋‚ธ๋‹ค.

  • ํ† ํฐ ๊ฒ€์ฆ : ์„œ๋ฒ„๋Š” JWT์˜ ์„œ๋ช…(Signature)์„ ํ™•์ธํ•˜๊ณ , ํ† ํฐ์ด ์œ ํšจํ•œ์ง€ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.
  • ์ธ๊ฐ€ ์ฒ˜๋ฆฌ : ํ† ํฐ ์œ ํšจ โ†’ ํด๋ผ์ด์–ธํŠธ์—์„œ ํ•ด๋‹น ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ๊ถŒํ•œ์ด ์žˆ๋Š”์ง€ ํ™•์ธ ํ›„, ์ž‘์—… ํ—ˆ์šฉ and ๊ฑฐ๋ถ€

๐Ÿ‘ฉ๐Ÿปโ€๐Ÿฆฐ ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด, ๐Ÿ”—Spring Security์™€ JWT ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์กฐํ•ฉํ•˜์—ฌ ๊ตฌํ˜„ํ•ด๋ณด์ž.

  1. Spring Security๋ฅผ ์‚ฌ์šฉ : ๋ณดํ˜ธ๋œ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์„ค์ •ํ•˜๊ณ , ํ•„ํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ JWT ๊ฒ€์ฆ์„ ์ˆ˜ํ–‰
  2. JWT ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ: JWT ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ† ํฐ ์ƒ์„ฑ, ๊ฒ€์ฆ, ํŒŒ์‹ฑ ๋“ฑ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰
  3. ์‚ฌ์šฉ์ž ์ธ์ฆ ๋ฐ ํ† ํฐ ๋ฐœ๊ธ‰: ๋กœ๊ทธ์ธ ์‹œ ์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•˜๊ณ , ์œ ํšจํ•œ ๊ฒฝ์šฐ JWT ํ† ํฐ์„ ๋ฐœ๊ธ‰ํ•ฉ๋‹ˆ๋‹ค.
  4. ํ† ํฐ ๊ฒ€์ฆ ๋ฐ ๊ถŒํ•œ ํ™•์ธ: ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณดํ˜ธ๋œ ์—”๋“œํฌ์ธํŠธ์— ์š”์ฒญํ•  ๋•Œ ํ† ํฐ์„ ๊ฒ€์ฆํ•˜๊ณ , ๊ถŒํ•œ์„ ํ™•์ธํ•˜์—ฌ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜ ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ–ฅ๏ธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž.

1. build.gradle

๋‹ค์Œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

dependencies {
  
    ...
    
    //security
	implementation 'org.springframework.boot:spring-boot-starter-security'
 
	// jwt
	implementation 'io.jsonwebtoken:jjwt-api'
	implementation 'io.jsonwebtoken:jjwt-impl'
	implementation 'io.jsonwebtoken:jjwt-jackson'
}

2. TokenInfo - jwt ์ƒ์„ฑ์„ ์œ„ํ•œ ์ •๋ณด

ํด๋ผ์ด์–ธํŠธ์— ํ† ํฐ ๋ณด๋‚ด๊ธฐ์œ„ํ•ด DTO ์ƒ์„ฑ

@Builder
@Data
@AllArgsConstructor
@ConfigurationProperties("jwt")
public class TokenInfo {
 
//    private String grantType;
//    private String accessToken;
//    private String refreshToken;
    
    private String issuer; 
    private String secretKey;
}

grantType์€ JWT ๋Œ€ํ•œ ์ธ์ฆ ํƒ€์ž…์œผ๋กœ, ์—ฌ๊ธฐ์„œ๋Š” Bearer๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์ดํ›„ HTTP ํ—ค๋”์— prefix๋กœ ๋ถ™์—ฌ์ฃผ๋Š” ํƒ€์ž…์ด๊ธฐ๋„ ํ•˜๋‹ค.

@ConfigurationProperties

ํ”„๋กœํผํ‹ฐ ํŒŒ์ผ์ด๋‚˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜์™€ ๊ฐ™์€ ์™ธ๋ถ€ ์„ค์ • ์†Œ์Šค์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์™€ Java ๊ฐ์ฒด์— ๋งคํ•‘ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

3. TokenProvider - ์•”ํ˜ธํ™”, ๋ณตํ˜ธํ™”

JWT ํ† ํฐ ์ƒ์„ฑ, ํ† ํฐ ๋ณตํ˜ธํ™” ๋ฐ ์ •๋ณด ์ถ”์ถœ, ํ† ํฐ ์œ ํšจ์„ฑ ๊ฒ€์ฆ์˜ ๊ธฐ๋Šฅ์ด ๊ตฌํ˜„๋œ ํด๋ž˜์Šค์ด๋‹ค.

๋จผ์ € applicatoin.yml์— ๋‹ค์Œ ์„ค์ •์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

jwt:
  issuer: (๋ฐœํ–‰์ธ)
  secret_key: secretKey

ํ† ํฐ์˜ ์•”ํ˜ธํ™” ๋ณตํ˜ธํ™”๋ฅผ ์œ„ํ•œ secret key๋กœ์„œ ์ดํ›„ HS256 ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด, 256๋น„ํŠธ๋ณด๋‹ค ์ปค์•ผํ•œ๋‹ค.
์•ŒํŒŒ๋ฒณ์€ ํ•œ๋‹จ์–ด ๋‹น 8bit ์ด๋ฏ€๋กœ 32๊ธ€์ž ์ด์ƒ์ด๋ฉด ๋œ๋‹ค.

JWT ํ† ํฐ ์ƒ์„ฑ ๋ฉ”์„œ๋“œ

@Service
@RequiredArgsConstructor
@Slf4j
public class TokenProvider {
  private final TokenInfo tokenInfo;
    
  /**
   * JWT ํ† ํฐ ์ƒ์„ฑ ๋ฉ”์„œ๋“œ
   * @param user - ํ† ํฐ์˜ ํด๋ ˆ์ž„(๋‚ด์šฉ)์— ํฌํ•จ๋  ๋กœ๊ทธ์ธ ์œ ์ € ์ •๋ณด
   * @return - ์ƒ์„ฑ๋œ JSON ์„ ์•”ํ˜ธํ™”ํ•œ ํ† ํฐ ๊ฐ’
   */
  public String createToken(User user) {

    log.info("CREATING TOKEN...");

	// ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ๊ฐ„ : 24H
    Date expiryDate = Date.from(    // `Instant`๋ฅผ `Date` ๋กœ ๋ณ€ํ™˜
        Instant.now()   // ํ˜„์žฌ ์‹œ๊ฐ„ ์ •๋ณด๋ฅผ `Instant`๋กœ ๊ฐ€์ ธ์˜จ๋‹ค
            .plus(1, ChronoUnit.DAYS) // ํ˜„์žฌ ์‹œ๊ฐ + 1์ผ
    );
    System.out.println("expiryDate = " + expiryDate);

    // ์ถ”๊ฐ€ ํด๋ ˆ์ž„ ์ •์˜
    Map<String, Object> claims = new HashMap<>();
    claims.put("email", user.getEmail());

    return Jwts.builder()
        // token header์— ๋“ค์–ด๊ฐˆ ์„œ๋ช… : ๋น„๋ฐ€๊ฐ’๊ณผ ํ•จ๊ป˜ ํ•ด์‹œ๊ฐ’์„ HS256 ๋ฐฉ์‹์œผ๋กœ ์•”ํ˜ธํ™”
        .signWith(
            tokenInfo.getSecretKey()
            , SignatureAlgorithm.HS256
        )
        .setHeaderParam(Header.TYPE, Header.JWT_TYPE) // ํ—ค๋” type : JWT
        .setClaims(claims) // ์ถ”๊ฐ€ํด๋ ˆ์ž„์€ ๋จผ์ € ์„ค์ •ํ•ด์•ผ ํ•จ
        // ์ถ”๊ฐ€ํ•˜๋Š” ํด๋ ˆ์ž„์ด ์ด๋ฏธ ํ† ํฐ์— ํฌํ•จ๋œ ๋‹ค๋ฅธ ์ •๋ณด๋“ค๊ณผ ์ถฉ๋Œํ•˜์ง€ ์•Š๋„๋ก ํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ
        .setIssuer(tokenInfo.getIssuer()) // ๋‚ด์šฉ iss
        .setIssuedAt(new Date()) // ๋‚ด์šฉ iat : ํ† ํฐ ๋ฐœ๊ธ‰ ์‹œ๊ฐ„
        .setExpiration(expiryDate) // ๋‚ด์šฉ exp : ์œ ํšจ ์‹œ๊ฐ„
        .setSubject(user.getId()) // sub: ํ† ํฐ์„ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์š”๋ฐ์ดํ„ฐ
        .compact();
    }

๐Ÿง JWT์˜ ๊ตฌ์กฐ

๐Ÿ”—(Header + Payload + Signature)

ํ† ํฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ

  /**
   * ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ „์†กํ•œ ํ† ํฐ์„ ๋””์ฝ”๋”ฉํ•˜์—ฌ ํ† ํฐ์˜ ์œ„์กฐ์—ฌ๋ถ€๋ฅผ ํ™•์ธ
   * ํ† ํฐ์„ json์œผ๋กœ ํŒŒ์‹ฑํ•ด์„œ ํด๋ ˆ์ž„(ํ† ํฐ์ •๋ณด)๋ฅผ ๋ฆฌํ„ด
   * @param token
   * @return - ํ† ํฐ ์•ˆ์—์žˆ๋Š” ์ธ์ฆ๋œ ์œ ์ €์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜
   */
  public TokenUserInfo validateAndGetTokenUserInfo(String token) {

    Claims claims = Jwts.parserBuilder()
        // ํ† ํฐ ๋ฐœ๊ธ‰์ž์˜ ๋ฐœ๊ธ‰ ๋‹น์‹œ์˜ ์„œ๋ช…์„ ๋„ฃ์–ด์คŒ
        .setSigningKey(tokenInfo.getSecretKey())
        // ์„œ๋ช… ์œ„์กฐ ๊ฒ€์‚ฌ: ์œ„์กฐ๋œ๊ฒฝ์šฐ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
        // ์œ„์กฐ๊ฐ€ ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ํŽ˜์ด๋กœ๋“œ๋ฅผ ๋ฆฌํ„ด
        .build()
        .parseClaimsJws(token)
        .getBody();

    log.info("claims: {}", claims);

    return TokenUserInfo.builder()
        .userId(claims.getSubject())
        .email(claims.get("email", String.class))
        .build();
  }

0๊ฐœ์˜ ๋Œ“๊ธ€