본문 바로가기

개발/Spring | Spring Boot

Spring Boot에서 JWT사용 법

1. 설정

https://jwt.io 메인페이지에서 아래와 같이 언어별 지원하는 암호화 방법 및 라이브러리를 확인할 수 있다.

이 글에서는 jjwt를 사용한다.

 

Spring에서 JWT를 사용하려면 먼저 maven에 Dependency를 추가해야 한다.

 

홈페이지에서 확인한 라이브러리 내용 중 가장 왼쪽에 있는 내용을 groupId에 중간 내용을 artifactId에 가장 오른쪽 내용을 version에 넣으면 된다. pom.xml에 아래와 같이 추가한다.

1
2
3
4
5
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

이후 maven update를 실행하면 JWT를 사용할 수 있다.(이클립스를 사용하는 경우 프로젝트에서 우클릭 -> Maven -> Update Project를 실행하면 된다.)

 

2. Token 생성

 
private static final String secretKey =  "secretKey"; //Token 체크시 필요한 암호키
    
public String createToken(){
    Calendar cal = Calendar.getInstance();
    cal.add(Calendar.DATE,1); //만료일 1일

    Claims claims = Jwts.claims()
        .setSubject("access_token")
        .setIssuedAt(new Date()); //생성일 설정
        .setExpiration(new Date(cal.getTimeInMillis())); //만료일 설정

    claims.put("key", "value"); //담고 싶은 값

    String jwt = Jwts.builder()
        .setHeaderParam("typ", "JWT")
        .setClaims(claims)
        .signWith(SignatureAlgorithm.HS256,  secretKey.getBytes())
        .compact();

    return jwt;
}

Token 생성 후 반환하는 Method 예시다.

1. Token은 String으로 구성되기 때문에 반환 타입이 String이다.

2. secretKey는 토큰 유효성 확인 시 사용하기 때문에 외부에 노출되지 않게 주의해야 한다.

3. claim은 payload 부분에 들어갈 정보 조각들이다.

4. claim은 Registered claim / Public claim / Private claim으로 나눠진다.

 4.1. Registered claim

  1) Registered claim은 Token에 대한 정보를 담기 위해 이미 정해진 claim이다. 

  2) Registered claim은 필수 값이 아닌 선택 값이다.

  3) Registered claim에는 Issuer claim, Subject claim, Audience claim, Expiration Time claim, Not Before

   claim, Issued At claim, JWT ID claim이 있다.

 4) 위의 예제에선 Subject(제목)과 Issued At(발행일), Expiration Time(만료일) claim을 설정하였다.

 4.2. Public claim

  1) 누구나 자유롭게 사용 가능하다.

  2) 때문에 충돌을 방지하기 위해  IANA JSON Web Token Registry 에 정의하거나, 충돌 방지 네임 스페이스가 충돌 방

     지 URI로 정의해야 한다.

 4.3. Private claim

  1) 협의 하에 사용되는 claim이다. Public claim과는 달리 key값이 중복되어 충돌될 수 있으니 유의해야 한다.

  2) 위의 예제에서 claims.put() 부분이다.

5. 위의 예제의 Method를 실행하면 Token이 생성된다.

 

3. Token 유효성 확인

 
private static final String secretKey =  "secretKey";

public boolean checkClaim(String jwt) {
    try {
        Claims claims = Jwts.parser()
            .setSigningKey(secretKey.getBytes())
            .parseClaimsJws(jwt).getBody(); 
        return true;
    
    }catch(ExpiredJwtException e) {   //Token이 만료된 경우 Exception이 발생한다.
        logger.error("Token Expired");
        return false;
    
    }catch(JwtException e) {        //Token이 변조된 경우 Exception이 발생한다.
        logger.error("Token Error");
        return false;
    }
}

Token 유효성을 확인하는 Method이다.

1. 문제가 없는 경우 true가 반환된다.

2. 만료 / 변조된 경우 false가 반환된다.

 

4. Token 정보 확인

 
private static final String secretKey =  "secretKey";

public Claims getJwtContents(String jwt) {
    Claims claims = Jwts.parser()
        .setSigningKey(secretKey.getBytes())
        .parseClaimsJws(jwt).getBody();
    return claims;
}

Token에 담긴 claim을 반환하는 Method이다.

1. 반환된 claim에 담긴 정보를 사용하려면 아래와 같이 하면 된다.

String value = (String)claims.get("key"); //Private claim의 경우
Date iat = claim.getIssuedAt(); //Registered claim 중 Issued At claim의 경우

//위의 코드와 같다.
String value = Jwts.parser()
    .setSigningKey(secretKey.getBytes())
    .parseClaimsJws(token)
    .getBody()
    .get("key");
    
Date iat = Jwts.parser()
    .setSigningKey(secretKey.getBytes())
    .parseClaimsJws(token)
    .getBody()
    .getIssuedAt();
​

 

위의 코드를 모두 합치면 아래와 같다.

public class Jwt {
    private static final String secretKey =  "secretKey";
    
    public String createToken(){
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DATE,1);

        Claims claims = Jwts.claims()
            .setSubject("access_token")
            .setIssuedAt(new Date());
            .setExpiration(new Date(cal.getTimeInMillis()));

        claims.put("key", "value");

        String jwt = Jwts.builder()
            .setHeaderParam("typ", "JWT")
            .setClaims(claims)
            .signWith(SignatureAlgorithm.HS256,  secretKey.getBytes())
            .compact();

        return jwt;
    }
        
    public boolean checkClaim(String jwt) {
        try {
            Claims claims = Jwts.parser()
                .setSigningKey(secretKey.getBytes())
                .parseClaimsJws(jwt).getBody(); 
            return true;
        
        }catch(ExpiredJwtException e) {
            logger.error("Token Expired");
            return false;
        
        }catch(JwtException e) {
            logger.error("Token Error");
            return false;
        }
    }
        
    public Claims getJwtContents(String jwt) {
        Claims claims = Jwts.parser()
            .setSigningKey(secretKey.getBytes())
            .parseClaimsJws(jwt).getBody();
        return claims;
    }
}