TIL - 23.01.02

0

TIL

목록 보기
45/126

오늘 팀프로젝트로 나는 회원가입 기능, 로그인 성공시 토큰생성과 유효성 검증인 JwtUtil을 작성하였다.

전부 다 스프링 심화과정에서 진행했던 MySelectShop을 참고해서 해보니 생각보다 할만했고, 내가 예전에 복습했던 졍규표현식을 물론 형식은 똑같았지만 이번에도 이해하며 사용해보았다.

JWT는 강의 때 이해가 하나도 되지 않았지만 이번에 코드를 써보면서 설명을 듣다보니 이것을 어떤식으로 사용하는지, 어떻게 구성되어있는지 정도로 이해하고 넘어가니 훨씬 수월했다.

이번에 약간 이해된부분들을 코드에 주석으로 잘 메모해두었기에 JwtUtil클래스의 모든 코드를 기록해두려한다.

package com.teamnull.blog.util.jwt;


import com.teamnull.blog.entity.enums.UserRoleEnum;
import com.teamnull.blog.util.security.UserDetailsServiceImpl;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.util.Base64;
import java.util.Date;


@Slf4j
@Component
@RequiredArgsConstructor
public class JwtUtil {

    private final UserDetailsServiceImpl userDetailsService;
    
    public static final String AUTHORIZATION_HEADER = "Authorization"; // Header에 들어가는 key 값
    public static final String AUTHORIZATION_KEY = "auth"; // 사용자 권한의 key 값
    private static final String BEARER_PREFIX = "Bearer "; // Token 앞에 붙는 식별자
    private static final long TOKEN_TIME = 60 * 60 * 1000L; // Token 만료 시간


    @Value("${jwt.secret.key}") // application.properties에 지정한 key 값을 가져온다
    private String secretKey;
    private Key key; // 토큰을 생성할 때 넣어줄 key 값
    private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // key 객체를 암호화할 알고리즘

    @PostConstruct // 객체가 생성될 때 초기화 해주는 기능
    public void init() {
        byte[] bytes = Base64.getDecoder().decode(secretKey); // Base64로 incoding 되어있는 상태를 decode 해주는 과정
        key = Keys.hmacShaKeyFor(bytes); // HMAC-SHA 알고리즘으로 디코드 된 바이트 값을 넣어 키를 생성
    }

    // 토큰 생성
    public String createToken(String username, UserRoleEnum role) {
        Date date = new Date();
        return BEARER_PREFIX +
                Jwts.builder()
                        .setSubject(username) // token 정보 안에 username을 넣어줌
                        .claim(AUTHORIZATION_KEY, role) // 권한 가져오기
                        .setExpiration(new Date(date.getTime() + TOKEN_TIME)) // 토큰 유효기간 : 현재 시간 + TOKEN_TIME
                        .setIssuedAt(date) // 토큰이 언제 만들어졌는지
                        .signWith(key, signatureAlgorithm) // secret key를 이용해 만든 key 객체를 어떠한 알고리즘을 통해 암호화 할 것인지 지정
                        .compact();
    }

    // Header에서 토큰 가져오기
    public String resolveToken(HttpServletRequest request) { // HttpServletRequest객체의 Header 안에 토큰이 들어있음
        String bearerToken = request.getHeader(AUTHORIZATION_HEADER); // AUTHORIZATION_HEADER를 파라미터로 Header에 있는 Token 값을 가져온다
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
            return bearerToken.substring(7);
        }
        return null;
    }

    // 토큰 검증
    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token); // 이렇게 코드를 넣으면 내부적으로 토큰을 검증
            return true;
        } catch (SecurityException | MalformedJwtException e) {
            log.info("유효하지 않은 JWT 서명 입니다.");
        } catch (ExpiredJwtException e) {
            log.info("만료된 JWT 토큰 입니다.");
        } catch (UnsupportedJwtException e) {
            log.info("지원되지 않은 JWT 토큰 입니다.");
        } catch (IllegalArgumentException e) {
            log.info("잘못된 JWT 토큰 입니다.");
        }
        return false;
    }

    // 토큰에서 사용자 정보 가져오기 // 위에서 이미 토큰 검증을 완료했다는 가정이므로 이 토큰은 유효하기 때문에 try-catch문이 없다
    public Claims getUserInfoFromToken(String token) {
        return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody(); // getBody를 통해 안에 정보들을 가져온다
    }
}

자신감이 벅차올라 이번에는 도전할 엄두도 못냈었던 security를 도전해보기로 했지만, 이거는 다시 공부해보려고 강의를 들어보았지만 이부분은 써먹는거에서부터 이론만 약간 이해될뿐 어떻게 써먹어야될지 감이 하나도 잡히지 않았다.

내일 새로운 마음으로 팀원의 도움을 받아 다시 도전해볼것이다.

0개의 댓글