mondegreen

JWT 토큰 만들다 10시간 동안 경주마된 썰 본문

기타/공통프로젝트_에러로그

JWT 토큰 만들다 10시간 동안 경주마된 썰

앙갱 2023. 7. 30. 23:04
반응형

JWTUtils.java

package com.kkosunnae.deryeogage.global.util;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Date;


@Slf4j
@Component
public class JwtUtil {

    @Value("${security.jwt.token.SK}")
    private String SK;

    @Value("${security.jwt.token.tokenValidTime}")
    private long tokenValidTime;

    // 토큰 생성

    public String createToken(String claimId, Long data) throws UnsupportedEncodingException {

        Date now = new Date();

        SecretKey secretKey = Keys.hmacShaKeyFor(SK.getBytes(StandardCharsets.UTF_8));

        String token = Jwts.builder()
                //.setHeaderParam("enc", "HS256")
                .setHeaderParam("typ","JWT")
                .claim("userId", data)
                .setIssuedAt(now)
                .setExpiration(new Date(now.getTime()+tokenValidTime))
                .signWith(secretKey, SignatureAlgorithm.HS256)
                .compact();


        return token;
    }

    // 토큰에서 회원 ID 추출
    public Long getUserId(String token){

        // 유효성 검사
        try {
            validateToken(token);
        } catch (RuntimeException e) {
            throw new RuntimeException("토큰에 문제가 있어요 Invalid token: " + e.getMessage());
        }

        //디코더 객체 생성
        Base64.Decoder decoder = Base64.getDecoder();

        //토큰에서 payload 추출
        final String[] splitJwt = token.split("\\.");

        //payload 디코딩
        final String payLoadStr = new String(decoder.decode(splitJwt[1].getBytes()));

        //디코딩된 문자열 JSON으로 변환
        JsonParser parser = new JsonParser();
        JsonObject jsonObject = parser.parse(payLoadStr).getAsJsonObject();

        //사용자의 id를 반환
        return jsonObject.get("userId").getAsLong();
    }

    // 유효성 검사
    public void validateToken(String token) throws RuntimeException {
        SecretKey secretKey = Keys.hmacShaKeyFor(SK.getBytes(StandardCharsets.UTF_8));

        try {
         Jwts.parserBuilder().setSigningKey(secretKey).build().parseClaimsJws(token);
        } catch (ExpiredJwtException e) {
            throw new RuntimeException("Expired JWT token: 토큰 만료");
        } catch (UnsupportedJwtException e) {
            throw new RuntimeException("Unsupported JWT token: 지원되지 않는 토큰");
        } catch (MalformedJwtException e) {
            throw new RuntimeException("Invalid JWT token: 유효하지 않은 토큰");
        } catch (SignatureException e) {
            throw new RuntimeException("Invalid JWT signature: 유효하지 않은 서명");
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("JWT claims string is empty.: claims이 비어있음");
        }
    }

}

 

BoardController.java

public class BoardController {

    private final JwtUtil jwtUtil;

    private final BoardService boardService;

    //글 작성 // Swagger테스트한다고 @requestBody 뺌
    @PostMapping("/boards")
    public Response<Object> saveBoard(@RequestHeader HttpHeaders header, @RequestBody BoardDto boardDto){

        String token = header.getFirst("accessToken");
        log.info("헤더에서 가져온 토큰 정보: "+ token);
        Long userId = jwtUtil.getUserId(token);

        boardDto.setUserId(userId);

        log.info("userId :", boardDto.getUserId());

        boardService.save(boardDto);
        return Response.success(null);
    }
  }

 

위 코드가 성공한 코드이다..

 

 

10시간 동안 경주마처럼 JWTUtils.java에서만 오류 원인을 찾고 있었고 실제 원인은 헤더에서 토큰 값을 가져오는 과정에 있었다. GetMapping인 경우 header에서 키값을 기준으로 value를 찾아서 toString해주면 되었다.

 

하지만 Post Mapping은 달랐다.....(헝) 

 

Spring Framework에서 HTTP 요청 헤더의 값을 가져오는 방식과 관련해 get 메소드는 HttpHeaders 객체에서 헤더의 값을 가져오지만, Post인 경우는 List<String> 타입을 반환한다. 따라서 toString() 메소드를 호출하면 token을 대괄호를 붙여서 가져오고.... 나의 유효성 검사는 끊임없이 서명 오류를 내었던 것이다... 헝...

 

 

따라서! toString대신 getFirst메서드를 활용해 첫번째 값을 가져오고 헤더값이 없는 경우 null을 반환한다..

고생했다 정말.

반응형