1. JWT 생성 및 검정

이용준·2023년 9월 25일
0

JWT

목록 보기
1/4

1. 토큰 생성 및 유효성 검정

  1. 개발자 되기 中
@RequiredArgsConstructor  
@Service  
public class TokenProvider {  
  
    @Value("${jwt.issuer}")  
    private String issuer;  
  
    @Value("${jwt.secret_key}")  
    private String secretKey;  
  
    public String generateToken(User user, Duration expiredAt){  
        Date now = new Date();  
        return makeToken(new Date(now.getTime()+expiredAt.toMillis()), user);  
    }  
  
    // 1.JWT 토큰 생성  
    private String makeToken(Date expiry, User user){  
        Date now = new Date();  
  
        return Jwts.builder()  
                .setHeaderParam(Header.TYPE, Header.JWT_TYPE) // 헤더 typ : JWT                .setIssuer(issuer) // 내용 iss : mail                .setIssuedAt(now) // 내용 iat : 현재 시간  
                .setExpiration(expiry) // 내용 exp : expiry 멤버 변숫값  
                .setSubject(user.getEmail()) // 내용 sub : 유저 이메일  
                .claim("id", user.getId()) // 클레임 id : 유저 ID
                .signWith(SignatureAlgorithm.HS256, secretKey) // 서명 : 비밀값과 해시값을 HS256 방식으로 암호화  
                .compact();  
    }  
  
    // 2. JWT 토큰 유효성 검증 메서드  
    public boolean validToken(String token){  
        try{  
            Jwts.parser()  
                    .setSigningKey(secretKey) // 비밃값으로 복호화  
                    .parseClaimsJws(token);  
             return true;        
        }catch (Exception e) { // 복호화 과정에서 에러 나면 유효하지 않은 토큰  
            return false;  
        }  
    }  
  
    // 3. 토큰 기반 인증 정보 가져오는 메서드  
    public Authentication getAuthentication(String token){  
        Claims claims = getClaims(token);  
        Set<SimpleGrantedAuthority> authorities = Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"));  
  
        return new UsernamePasswordAuthenticationToken(new org.springframework.security.core.userdetails.User(claims.getSubject(),  
                "", authorities),token, authorities);  
    }  
  
    // 4. 토큰 기반으로 유저ID 가져오는 메서드  
    public Long getUserId(String token){  
        Claims claims = getClaims(token);  
        return claims.get("id", Long.class);  
    }  
  
    private Claims getClaims(String token) {  
        return Jwts.parser()  
                .setSigningKey(secretKey)  
                .parseClaimsJws(token)  
                .getBody();  
    }
@RequiredArgsConstructor
@Service
public class TokenProvider{
   private final 토큰 제공자


	public String 토큰발급(User user, Duration expiredAt){
	  Date now = new Date(); // 유효 기간 설정 목적
	  return 토큰생성(new Date(now.getTime() + expiredAt.toMillis()), user);
	}
	
	public String 토큰생성(Date expiry, User user){
	  Date now = new Date(); 
	  
	  return Jwts.builder() // 필요한 정보 설정
	    ...
	    .compact();
	}
	
	public boolean 유효성 검증(String token){
	  try{
	    Jwts.parser()
	      .비밀키 복호화
	      .토큰 jws로 파싱
	    return true
	  }catch (Exception e){
	    return false
	  }
	}
	
	public Authentication 인증정보(String token){
	  클레임 = 클레임가져오기(token);
	  인증정보
	  
	  return 이메일, 토큰, 인증정보
	
	}
	
	public Long 유저Id(String token){
	  클레임 = 클레임가져오기(token);
	  return 클레임.get("id", Long.class);
	}
	
	public Claims 클레임가져오기(String){
	  return Jwts.parser()
	    .setSigningKey(토큰제공자.getSecretKey())
	    .parserClaimsJws(token)
	    .getBody();
	}
}
  1. 코드로 배우는 스프링 부트 中
package com.example.api.security.util;  
  
import io.jsonwebtoken.Jwts;  
import io.jsonwebtoken.SignatureAlgorithm;  
import io.jsonwebtoken.impl.DefaultClaims;  
import io.jsonwebtoken.impl.DefaultJws;  
import lombok.extern.log4j.Log4j2;  
  
import java.time.ZonedDateTime;  
import java.util.Date;  
  
@Log4j2  
public class JWTUtil {  
    private String secretKey = "test1234";  
  
    // 1Month  
    private long expire = 60*24*30;  
  
    // JWT 토큰 생성  
    public String generateToken(String content) throws Exception{  
  
        return Jwts.builder()  
                .setIssuedAt(new Date())  
//                .setExpiration(Date.from(ZonedDateTime.now().plusSeconds(1).toInstant())) // Exception 발생  
                .setExpiration(Date.from(ZonedDateTime.now().plusMinutes(expire).toInstant()))  
                .claim("sub", content)  
                .signWith(SignatureAlgorithm.HS256, secretKey.getBytes("UTF-8"))  
                .compact();  
    }  
  
    // JWT 문자열 검증 (인코딩된 문자열에서 원하는 값 추출)  
    public String validateAndExtract(String tokenStr) throws Exception{  
        String contentValue = null;  
  
        try{  
            DefaultJws defaultJws = (DefaultJws) Jwts.parser().setSigningKey(secretKey.getBytes("UTF-8")).parseClaimsJws(tokenStr);  
  
            log.info(defaultJws);  
            log.info(defaultJws.getBody().getClass());  
  
            DefaultClaims claims = (DefaultClaims) defaultJws.getBody();  
  
            log.info("=".repeat(30));  
            contentValue = claims.getSubject();  
  
        }catch(Exception e){  
            e.printStackTrace();  
            log.error(e.getMessage());  
            contentValue=null;  
        }  
        return contentValue;  
    }  
}

1. 토큰 생성

  1. 개발자 되기

@Value("${jwt.issuer}")  
private String issuer;  
  
@Value("${jwt.secret_key}")  
private String secretKey;

// JWT 생성
public String generateToken(User user, Duration expiredAt){
	Date date = new Date();
	return makeToken(new Date(now.getTime() + expiredAt.toMillis()), user);
}

public String makeToken(Date expiry, User user){
  Date now = new Date();

  return Jwts.builder()
	  .setHeaderParam(Header.TYPE, Header.JWT_TYPE)
	  .setIssuer(issuer)
	  .setIssuedAt(now)
	  .setExpiration(expiry) 
	  .setSubject(user.getEmail())
	  .clain("id", user.getId())
	  .signWith(SignatureAlgorithm.HS256, secretKey)
	  .compact();
}
  1. 코드로 배우는 스프링부트
private String secretKey = "test123";

// 1Month
Private Long expire = 60*24*30;

// JWT 생성
public String generateToken(String content) throws Exception{
	return Jwts.builder()
		.setIssuedAt(new Date)
		.setExpiration(Date.from(ZoneDateTime.now().plusMinutes(expire).toInstant()))
		.calim("sub", content) // content == user.getEmail
		.signWith(SignatureAlgorithm.HS256, secretKey.getBytes("UTF-8"))
		.compact();
}
  • 개발자 되기 : makeToken 통해 토큰 생성한 뒤 generateToken으로 만료 기간 지정하는 방법
  • 코드로 배우는 ~ : generateToken 통해 사용할 만료 기간까지 한 번에 설정하는 방법

객체 분리를 위해서는 전자의 방법이 좋지만, 실제 사용시 후자의 방법이 조금 더 간편하지 않을까 생각된다.


2. 유효성 검정 및 값 추출

  1. 개발자 되기
public boolean validToken(String token){
  try{
	  Jwts.parser()
		  .setSigningKey(secretKey) // 비밀값으로 복호화
		  .parseClaimsJws(token);
	  return true;
  }catch (Exception e){ // 위 과정에서 오류 발생시 유효하지 않은 토큰
	  return false;
  }
}

// 토큰 기반 인증 정보 가져오는 메서드
public Authentication getAuthentication(String token){
  Claims claims = getClaims(token);
  Set<SimpleGrantedAuthority> authorities = Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"));

  Return new UsernamePasswordAuthenticationToken(new User(claims.getSubject(), "", authorities), token, authorities); // 유저 이메일(sub), 패스워드, 인가정보 등을 기반으로 인증 정보 생성
}

// 토큰 기반 유저ID 가져오는 메서드
public Long userId(String token){
	Claims claims = getClaims(token);
	return claims.get("id", Long.class);
}

private Claims getClaims(String token){
  return Jwts.parser()
	  .setSigningKey(secretKey)
	  .parserClaimsJws(token)
	  .getBody();
}
  1. 코드로 배우는 스프링 부트
public String validateAndExtract(String tokenStr) throws Exceptions{
  String contentValue = null;

	try{
		DefaultJws defaultJws = (DefaultJws) Jwts.parser()
		                    .setSigningKey(secretKeys.getBytes("UTF-8"))
		                    .parseClaimsJws(tokenStr);
		
		log.info(defaultJws);
		log.info(defaultJws.getBody().getClass());
		
		DefaultClaims claims = (DefaultClaims) defaultJws.getBody();
		
		contentValue = claims.getSubject(); // 사용자 이메일(unique)
	
	}catch (Exception e){
		e.printStackTrace();
		log.error(e.getMessage());
		contentValue=null
	}
	return contentValue;
}
  • DefaultJws
public class DefaultJws<B> implements Jws<B> {  
  
    private final JwsHeader header;  
    private final B body;  
    private final String signature;  
  
    public DefaultJws(JwsHeader header, B body, String signature) {  
        this.header = header;  
        this.body = body;  
        this.signature = signature;  
    }

	@Override  
	public JwsHeader getHeader() {  
	    return this.header;  
	}  
	  
	@Override  
	public B getBody() {  
	    return this.body;  
	}  
	  
	@Override  
	public String getSignature() {  
	    return this.signature;  
	}  
	  
	@Override  
	public String toString() {  
	    return "header=" + header + ",body=" + body + ",signature=" + signature;  
	}
}
  • DefaultJws는 header, body, signature 로 구성된 클래스

1) 인증 정보 가져오기

  1. 개발자 되기

// 토큰 기반 인증 정보 가져오는 메서드
public Authentication getAuthentication(String token){
  Claims claims = getClaims(token);
  Set<SimpleGrantedAuthority> authroires = Collections.singleton(new SimpleGratnedAuthority("ROLE_USER"));

  return new UsernamePasswordAuthenticationToken(new User(claims.getSubject,"",authroities), token, authorities);
}

// 유저 ID 
public Long getUserId(String token){
  Claims claims = getClaims(token);
  return claims.get("id", Long.class);
}

private Claims getClaims(String token){
  return Jwts.parser()
	  .setSigningKey(secretKey)
	  .parseClaimsJws(token)
	  .getBody();
}
  1. 코드로 배우는 스프링부트
public String validtedAndExtract(String token) throws Exception{

  try{
    DefaultJws defaultJws = (DefaultJws) Jwts.parser()
						    .setSigningKey(secretKey.getBytes("UTF-8"))
						    .parseClaimsJws(tokenStr);

	DefaultClaims claims = (DefaultClaims) defaultJws.getBody();

	contentValue = claims.getSubject();
  
  }catch(Exception e){
    ...
  }
  return contentValue
}

2번째 방법은 검증 및 값 추출이 동시에 가능하므로 더 효율적으로 보임.

profile
뚝딱뚝딱

0개의 댓글