[260724]JWT 사용해보기

이상민·2026년 7월 24일

Spring

목록 보기
45/59

Gradle 세팅

Gradle에서는 위 이미지에 써져있는 jjwt부분을 추가해주면 된다.

JWT 코드

파일구조

JwtUtil코드

    public static final String BEARER_PREFIX = "Bearer ";
    private static final long TOKEN_TIME = 60 * 60 * 1000L; // 60분

    @Value("${jwt.secret.key}")           // application.yml 에 있는 key 가져옴
    private String secretKeyString;

    private SecretKey key;
    private JwtParser parser;

	// Bean을 초기화하는 메서드 PostConstruct 어노테이션으로 인해 코드 실행시 제일 먼저 실행
    @PostConstruct
    public void init() {
        byte[] bytes = Decoders.BASE64.decode(secretKeyString);
        this.key = Keys.hmacShaKeyFor(bytes);
        this.parser = Jwts.parser()
            .verifyWith(this.key)
            .build();
    }


    // 토큰 생성
    public String generateToken(String username) {
        Date now = new Date();
        return BEARER_PREFIX + Jwts.builder()
            .claim("username", username)
            .issuedAt(now)
            .expiration(new Date(now.getTime() + TOKEN_TIME))
            .signWith(key, Jwts.SIG.HS256)
            .compact();
    }

    // 토큰 검증
    public boolean validateToken(String token) {
        if (token == null || token.isBlank()) return false;
        try {
            parser.parseSignedClaims(token);
            return true;
        } catch (JwtException | IllegalArgumentException e) {
            // 개별 예외 분리 없음: 서명/형식/만료 등 모든 실패를 한 번에 처리
            log.debug("Invalid JWT: {}", e.toString());
            return false;
        }
    }



    // 토큰 복호화
    private Claims extractAllClaims(String token) {
        return parser.parseSignedClaims(token).getPayload();
    }

    public String extractUsername(String token) {
        return extractAllClaims(token).get("username", String.class);
    }

JwtUtil의 코드같은 경우 제공해주는 기본 코드만 복사 붙여넣기 해주면 되어 간단하다.
필요하다면, 프로젝트에 맞게 Secret Key, 토큰 만료 시간, claim 정도만 설정하면 된다.

public static final String BEARER_PREFIX = "Bearer ";
private static final long TOKEN_TIME = 60 * 60 * 1000L; 

집중해야되는 부분이 있다면, TOKEN_TIME의 경우에는 토큰의 유효시간을 결정해주는 코드이다.
BEARER_PREFIX의 경우 토큰이

Authorization: Bearer asdfasdfaasdfa...

이런식으로 생겼기 때문에, 실제 Jwt값을 제외한 Bearer부분을 제거하기 위해 생성한다.

토큰의 값을 추가하고 싶다면

    public String generateToken(String username) {
        Date now = new Date();
        return BEARER_PREFIX + Jwts.builder()
            .claim("username", username)
            .issuedAt(now)
            .expiration(new Date(now.getTime() + TOKEN_TIME))
            .signWith(key, Jwts.SIG.HS256)
            .compact();
    }

부분의 .claim에 값을 추가해주면 된다.


JwtFilter

@Component
@Slf4j
@RequiredArgsConstructor
public class JwtFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

        // JWT 검증이 필요 없는 경우 ex)로그인
        String requestURI = request.getRequestURI();

        if(requestURI.equals("/api/user/login")) {
            filterChain.doFilter(request,response);
            return;
        }

        // JWT 토큰이 있는지 없는지 검사
        String authorizationHeader = request.getHeader("Authorization");


        if (authorizationHeader == null || !authorizationHeader.startsWith(BEARER_PREFIX)) {
            log.info("JWT 토큰이 필요 합니다.");
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "JWT 토큰이 필요 합니다.");
            return;
        }


        // JWT 토큰이 있다면 유효한 토큰인지 검사
        String jwt = authorizationHeader.substring(7);

        if (!jwtUtil.validateToken(jwt)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.getWriter().write("{\"error\": \"Unauthorized\"}");
        }

        // JWT 토큰에서 복호화 한 데이터 저장하기
        String username = jwtUtil.extractUsername(jwt);

        request.setAttribute("username", username);

        filterChain.doFilter(request, response);


    }
}

JwtFilter의 경우 로그인 요청을 시도할 시 JWT가 발급되지 않은 상태이므로 토큰 검증을 수행할 필요가 없기 때문에, 해당 api요청은 검증하지 않도록 예외처리한다.

또한 request.setAttribute("username", username)을 통해 username을 request로 저장한다. 이를 통해 다시 토큰을 복호화 하지 않아도 사용자 정보를 사용할 수 있게된다.


UserController

@RestController
@RequestMapping("/api/user")
@RequiredArgsConstructor
@Slf4j
public class UserController {

    private final JwtUtil jwtUtil;

    @GetMapping("/get")
    public String getUserInfo(HttpServletRequest request) {
        log.info("호출 : " + request.getAttribute("username"));
        return request.getAttribute("username") + "이 호출 되었습니다..";
    }

    // 토큰 생성 테스트
    @PostMapping("/login")
    public ResponseEntity<LoginResponseDto> login() {

        String token  = jwtUtil.generateToken("kim-dong-hyun");

        return ResponseEntity.ok(new LoginResponseDto(token));
    }

    // 토큰 검증 테스트
    @GetMapping("/validate")
    public ResponseEntity<Boolean> checkValidate(HttpServletRequest request) {

        String authorizationHeader = request.getHeader("Authorization");

        String jwt = authorizationHeader.substring(7);

        Boolean validate = jwtUtil.validateToken(jwt);

        return ResponseEntity.ok(validate);
    }

    // 토큰 복호화 테스트
    @GetMapping("/username")
    public ResponseEntity<String> getUsername(HttpServletRequest request) {

        String authorizationHeader = request.getHeader("Authorization");

        String jwt = authorizationHeader.substring(7);

        String username = jwtUtil.extractUsername(jwt);

        return ResponseEntity.ok(username);
    }

}

마지막으로 Controller이다.

request.getHeader("Authorization")으로 헤더를 가져오고,
ahtorizationHeader.substring(7)를 사용하여 위의 Bearer부분을 삭제하고 실제 필요로 하는 JWT만 추출한다.
validateToken의 경우 토큰의 서명, 형식, 만료 여부 등을 검사하여 모두 통과될시 true를 반환한다.

Spring에서의 JWT를 사용할때는 발급->검증만 외우면 된다.

로그인에 성공시 JWT를 발급 -> 모든 요청의 Authorization Header에 JWT를 함께 전송 -> JwtFilter를 통해 토큰의 유효성 검증

JWT의 동작 방식만 이해한다면 실제 사용은 전혀 어려움이 없다.

profile
백앤드 개발 브이로그

0개의 댓글