JWT 생성 및 인증

안대현·2022년 5월 18일
3
post-custom-banner

JWT를 발급받고 인증해보자!

1. JWT 인증

  • JWT란?

JWT는 Json Web Token으로, 인증에 필요한 정보들을 토큰에 담아 암호화시켜 사용하는 방식이다. 서명된 토큰이라는 점에서 쿠키보다 더 보안적이라고 할 수 있다.

  • JWT의 구조
    JWT는 Header, Payload, Signature의 구성 요소로 이루어지며 각 구성 요소가 마침표(.)로 구분된다.

    Header에는 보통 토큰의 타입이나, 서명 생성에 사용되는 알고리즘을 저장한다.
    Payload에는 토큰에 담을 정보를 key-value 형태로 저장한다.
    Signature에는 서명이 저장되는데 암호화되어 있기 때문에 서버에 있는 개인 키로만 복호화가 가능하다.

JWT의 구조는 jwt.io 사이트에서 직접 데이터를 암호화하며 확인해볼 수 있다.

2. JWT 생성하기

JWT를 생성하는 방법은 아주 간단하다.
아래 코드는 JWT 생성 코드로, createJwt 메소드에서 builder 패턴을 이용해 JWT를 생성한다.

  • setHeaderParam()에서는 type이 jwt임을 설정해준다.
  • claim()에는 Payload에 담길 데이터를 입력한다. 여기서는 매개 변수로 전달 받은 id를 추가하였다.
  • setIssuedAt()은 발급 시간으로 현재 시간을 입력한다.
  • setExpiration()은 해당 JWT의 만료 시간으로, 데모 버전이라면 넉넉하게 설정해두는 것이 테스트하기 편할 것이다. 아래 코드에서는 오늘 날짜로부터 1년 뒤로 설정되어 있다.
  • signWith()에는 서명 알고리즘과 비밀 키가 들어간다. 서명 알고리즘으로 HS256을 선택한다면 비밀 키는 별도의 클래스에서 관리한다. 비밀 키는 공개되어서는 안되기 때문에 깃 허브에 올릴 때는 해당 파일을 .gitignore에 추가하도록 하자.
  • compact()는 위 설정대로 JWT 토큰을 생성한다는 의미이다.
    // TODO: JWT 생성
    public String createJwt(int id){
        Date now = new Date();
        return Jwts.builder()
                .setHeaderParam("type","jwt")
                .claim("id", id)
                .setIssuedAt(now)
                .setExpiration(new Date(System.currentTimeMillis()+1*(1000*60*60*24*365)))
                .signWith(SignatureAlgorithm.HS256, SecretKey.JWT_SECRET_KEY)
                .compact();
    }

비밀 키 예시)

public class SecretKey {
    public static String JWT_SECRET_KEY = "236979CB6F1AD6B6A6184A31E6BE37DB3818CC36871E26235DD67DCFE4041492";
}

이제 userIdx를 1로 설정한 뒤 JWT 토큰을 생성하면 다음과 같이 출력될 것이다.

int userIdx = 1;
String jwt = jwtService.createJwt(userIdx);
System.out.println("JWT : " + jwt);

출력 결과 > JWT : eyJ0eXBlIjoiand0IiwiYWxnIjoiSFMyNTYifQ.eyJpZCI6MSwiaWF0IjoxNjUyODcwNjY4LCJleHAiOjE2NTQzNDE4OTd9.5qXqkxjKA48g3s5LouLrfbnYZzs6rvIzSZenMtllULk

3. JWT에서 값 추출하기

JWT 생성을 하였으니 이제 인증을 할 차례이다.

프론트에서 JWT를 "X-ACCESS-TOKEN"이라는 이름의 Header에 담아 넘겨준 상황이라면 아래 코드와 같이 헤더에서 JWT 토큰을 추출할 수 있다.

    // TODO: 헤더에서 JWT 추출
    public String getJwt(){
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
        return request.getHeader("X-ACCESS-TOKEN");
    }

위의 getJwt() 메소드로 헤더에서 JWT 토큰을 추출했다면 다음은 JWT 토큰 안의 데이터를 파싱한다.
이때, 토큰 생성에 사용된 비밀키를 이용한다.
claim 파싱이 되면 JWT 토큰 생성에 추가하였던 id 값을 getBody().get() 함수로 추출할 수 있다.

    final String INVALID_JWT = "유효하지 않은 토큰입니다.";
    final String EMPTY_JWT = "토큰이 비어있습니다.";

    // TODO: JWT에서 값 추출
    public int getUserId() throws Exception{
        // 헤더에서 JWT 추출
        String accessToken = getJwt();
        if(accessToken == null || accessToken.length() == 0){
            throw new Exception(EMPTY_JWT);
        }

        // JWT 파싱
        Jws<Claims> claims;
        try{
            claims = Jwts.parser()
                    .setSigningKey(SecretKey.JWT_SECRET_KEY)
                    .parseClaimsJws(accessToken);
        } catch (Exception ignored) {
            throw new Exception(INVALID_JWT);
        }

        // id 추출
        return claims.getBody().get("id",Integer.class);
    }

사용 예시)

        try {
            //jwt에서 id 추출.
            int userIdByJwt = jwtService.getUserId();
            //추출한 ID와 접근한 유저의 ID가 같은지 확인
            if(userId != userIdByJwt){
                //TODO: 불일치
            }
            //TODO: 인증 성공 후 진행할 코드 구현
        } catch (Exception exception) {
        	//TODO: 오류 코드 반환
        }
profile
백엔드 개발자로 향하는 계단을 오르고 있습니다! 😎
post-custom-banner

0개의 댓글