본문 바로가기
웹개발/보안

jwt 토큰 - java로 생성하기 예제

by 인생여희 2021. 3. 8.

jwt 토큰 - java로 생성하기 예제

 

 

1.스프링 프로젝트 생성

 

2.jwt 라이브러리 추가

 

		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>
			<version>0.9.1</version>
		</dependency>

 

 

3.Utils 클래스 추가

 

JsonUtil.java

 

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;

@Component
public class JsonUtil {

	@Bean
	public ObjectMapper objectMapper() {
		return new ObjectMapper();
	}
}

 

 

JWTException.java

 

public class JWTException extends RuntimeException {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public JWTException() {
		super();
	}

	public JWTException(String message, Throwable cause) {
		super(message, cause);
	}

	public JWTException(String message) {
		super(message);
	}

	public JWTException(Throwable cause) {
		super(cause);
	}

}

 

 

4.VO 객체 추가

 

import java.io.Serializable;
import java.util.List;

public class User implements Serializable{
    
	private static final long serialVersionUID = 1L;
	
	//아이디
	private String userId;
	
	//비번
    private String password;
    
    //권한
    private List<String> authority;
    
    //사용여부
    private boolean enabled;
    
    //이름
    private String name;
    
    
    
	public String getUserId() {
		return userId;
	}
	public void setUserId(String userId) {
		this.userId = userId;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public List<String> getAuthority() {
		return authority;
	}
	public void setAuthority(List<String> authority) {
		this.authority = authority;
	}
	public boolean isEnabled() {
		return enabled;
	}
	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "User [userId=" + userId + ", password=" + password + ", authority=" + authority + ", enabled=" + enabled
				+ ", name=" + name + "]";
	}
    
}

 

 

5.JWT 토큰 생성 검증 클래스 추가

 

JwtService.java

 

import java.io.UnsupportedEncodingException;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sujin.app.dto.User;
import com.sujin.app.exception.JWTException;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@Service
public class JwtService{

	//비밀키
	private static final String ENCRYPT_STRING =  "pretty";
	
	//로그
	private static final Logger LOGGER  = LoggerFactory.getLogger(JwtService.class);
	
	// key 
	private static final String DATA_KEY = "user";
	
	
	//매퍼
	@Autowired
	private ObjectMapper objectMapper;
	
	
	//[0] User 정보를 이용해서 - JWT 생성
	public String createLoginToken(User user) {
		
		//현재시간
		long curTime = System.currentTimeMillis();
		
		//[1] Jwts 라이브러리로부터 jwt 생성 - builder 패턴
		
		return  Jwts.builder()
				
				.setSubject("Test JWT")
				
				//[2]
				//setHeaderParam 메소드를 통해 JWT 헤더가 지닐 정보들을 담는다.
				//alg 의 경우는 default 값이 SHA256이므로 따로 설정할 필요는 없다.
				//typ 를 셋팅 안해주면 오류 발생한다.
				
				 .setHeaderParam("typ", "JWT")
				 
				 //[3] 만료 시간
				 .setExpiration(new Date(curTime + 3600000))
				 
				 //[4] 발급 시간 
				 .setIssuedAt(new Date(curTime))
				 
				 
				 //[5] Payload 에 Private Claims 를 담기 위해 claim 메소드를 이용한다.
				 // private claim으로 VO객체를 추가할 수 있음
				 .claim(DATA_KEY, user)
				 
				 
				 //[6] 복호화 할때 사용하는 시그니처 설정.
				 // header의 인코딩값 + payload의 인코딩값 + 비밀키 = 시그니처
				 // signWith api는 해싱알고리즘과 비밀키가 필요하다.
				 .signWith(SignatureAlgorithm.HS256, this.generateKey())
				 
				 //생성
				 .compact();
		
		
		//"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MTQ5MzMwMzcsImlhdCI6MTYxNDkyOTQzNywidXNlciI6eyJ1c2VySWQiOiJzdWppbiIsInBhc3N3b3JkIjpudWxsLCJhdXRob3JpdHkiOlsiVVNFUiJdLCJlbmFibGVkIjpmYWxzZSwibmFtZSI6IuydtOyImOynhCJ9fQ.FsP6XGQ2tLJ9kO8NZMOP3OtZu69YK1vxWhNK4XGyEmU";

		
	}


	//비밀키 생성 메소드
	private byte[] generateKey(){
		byte[] key = null;
		try {
			
			
			//ENCRYPT_STRING =  pretty
			key = ENCRYPT_STRING.getBytes("UTF-8");
		} catch (UnsupportedEncodingException e) {
			LOGGER.error("Making secret Key Error :: ", e);
		}
		
		
		System.out.println("비밀 key : " + key); //[B@59252cb6
		
		
		return key;
	}
	
	
	
	
	//JWT 복호화
	public User getUser(String jwt) {
		
		//결과값 = Claims
		Jws<Claims> claims = null;
		
		try {
			
			//비밀키를 이용해서 복호화 하는 작업
			//jwt가 유효한지, 위변조 되지 않았는지 판단한다.
			//이 비밀키는 서버에만 존재해야되고, 유출되어서는 안된다.
			claims = Jwts.parser()
						 .setSigningKey(this.generateKey())
						 .parseClaimsJws(jwt);
			
			
		} catch (Exception e) {
			LOGGER.debug(e.getMessage(), e);
			throw new JWTException("decodeing failed");
		}
		
		//반환 타입은 LinkedHashMap 이다. 이를 User 타입으로 변환하기 위해 ObjectMapper 사용
		return objectMapper.convertValue(claims.getBody().get(DATA_KEY), User.class);
	}
}

 

 

6.호출

 

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.sujin.app.dto.User;
import com.sujin.app.service.JwtService;

@SpringBootApplication
public class SpringbootJwtExampleApplication implements CommandLineRunner{
	
	
	@Autowired
	private JwtService jwtService; 
	
	private static final Logger LOGGER = LoggerFactory.getLogger(SpringbootJwtExampleApplication.class);
	public static void main(String[] args) {
		SpringApplication.run(SpringbootJwtExampleApplication.class, args);
	}
	
	
	
	@Override
	public void run(String... args) throws Exception {
		
		
//		User u = new User();
//		u.setUserId("sujin");
//		u.setName("kimdongyuel");
//		u.setAuthority(Arrays.asList("USER"));
//		
//		
//		LOGGER.debug("creating jwt...");
//		
//		String token = jwtService.createLoginToken(u);
//		LOGGER.debug("jwt 생성 :"  +  token);
//		// jwt 생성 :eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MTQ5MzMwMzcsImlhdCI6MTYxNDkyOTQzNywidXNlciI6eyJ1c2VySWQiOiJzdWppbiIsInBhc3N3b3JkIjpudWxsLCJhdXRob3JpdHkiOlsiVVNFUiJdLCJlbmFibGVkIjpmYWxzZSwibmFtZSI6IuydtOyImOynhCJ9fQ.FsP6XGQ2tLJ9kO8NZMOP3OtZu69YK1vxWhNK4XGyEmU
//		
		
		
		String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MTQ5MzMwMzcsImlhdCI6MTYxNDkyOTQzNywidXNlciI6eyJ1c2VySWQiOiJzdWppbiIsInBhc3N3b3JkIjpudWxsLCJhdXRob3JpdHkiOlsiVVNFUiJdLCJlbmFibGVkIjpmYWxzZSwibmFtZSI6IuydtOyImOynhCJ9fQ.FsP6XGQ2tLJ9kO8NZMOP3OtZu69YK1vxWhNK4XGyEmU";
		
		LOGGER.debug("jwt decoding... ");
		User user = jwtService.getUser(token);
		LOGGER.debug("디코드된 jwt : " +  user);
	}

}

 

참고

 

- JWT를 생성할 때와 복호화할 때의 비밀키를 다르게 설정: SignatureException 발생

 

- 위조한 JWT에 대해 복호화를 시도:  MalformedJwtException 발생

 

- 만료기간이 지난 JWT에 대해 복호화를 시도:  ExpiredJwtException 발생

 

 

JWT-Authentication-master.zip
0.03MB

 

springboot-jwt-example-master.zip
0.09MB

 

 

 

참고 블로그

https://bamdule.tistory.com/123

https://medium.com/@OutOfBedlam/jwt-%EC%9E%90%EB%B0%94-%EA%B0%80%EC%9D%B4%EB%93%9C-53ccd7b2ba10

Refresh token은 필요한가? : https://zzossig.io/posts/etc/what_is_the_point_of_refresh_token

[Spring Security] OAuth2 방식으로 JWT사용해보기(accesstoken, refreshtoken) : https://fenderist.tistory.com/353