From aceda13cb613bb86ede90d2c4870b2b351456903 Mon Sep 17 00:00:00 2001 From: youjin09222 Date: Fri, 10 Nov 2023 23:12:52 +0900 Subject: [PATCH] feat: Add JwtTokenProvider (#3) --- .../jwt/JwtTokenProvider.java | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 server-toy-project/src/main/java/com/gdsc_teamb/servertoyproject/jwt/JwtTokenProvider.java diff --git a/server-toy-project/src/main/java/com/gdsc_teamb/servertoyproject/jwt/JwtTokenProvider.java b/server-toy-project/src/main/java/com/gdsc_teamb/servertoyproject/jwt/JwtTokenProvider.java new file mode 100644 index 0000000..d09bd2c --- /dev/null +++ b/server-toy-project/src/main/java/com/gdsc_teamb/servertoyproject/jwt/JwtTokenProvider.java @@ -0,0 +1,113 @@ +package com.gdsc_teamb.servertoyproject.jwt; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; +import java.security.Key; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class JwtTokenProvider { + + private final Key key; + + public JwtTokenProvider(@Value("${jwt.secret}") String secretKey) { + byte[] keyBytes = Decoders.BASE64.decode(secretKey); // Base64 형식으로 인코딩된 Secret Key를 디코딩한 후, byte array를 반환 + this.key = Keys.hmacShaKeyFor(keyBytes); // HMAC 알고리즘을 적용 + } + + // 유저 정보를 가지고 AccessToken, RefreshToken 생성 + public TokenInfo generateToken(Authentication authentication) { + // 권한 가져오기 + String authorities = authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .collect(Collectors.joining(",")); + + long now = (new Date()).getTime(); + + // Access Token 생성 + Date accessTokenExpiresIn = new Date(now + 1000 * 60 * 60); // 토큰 만료 시간 (1h) + + String accessToken = Jwts.builder() + .setSubject(authentication.getName()) // 토큰 제목 + .claim("auth", authorities) // JWT에 포함 시킬 Custom Claim를 추가 (인증된 사용자와 관련된 정보 추가) + .setExpiration(accessTokenExpiresIn) // 토큰 만료 시간 설정 + .signWith(key, SignatureAlgorithm.HS256) // Sign을 위한 Key 객체 설정 (암호화 알고리즘 + secretKey = signature) + .compact(); // 토큰 생성 및 직렬화 + + // Refresh Token 생성 + String refreshToken = Jwts.builder() + .setExpiration(new Date(now + 1000 * 60 * 60 * 24 * 7)) // 토큰 만료 시간 (7d) + .signWith(key, SignatureAlgorithm.HS256) + .compact(); + + return TokenInfo.builder() + .grantType("Bearer") + .accessToken(accessToken) + .refreshToken(refreshToken) + .build(); + } + + // JWT 토큰을 복호화하여 토큰에 들어있는 정보를 꺼내는 메서드 + public Authentication getAuthentication(String accessToken) { + // 토큰 복호화 + Claims claims = parseClaims(accessToken); + + if (claims.get("auth") == null) { + throw new RuntimeException("권한 정보가 없는 토큰입니다."); + } + + // 클레임에서 권한 정보 가져오기 + Collection authorities = + Arrays.stream(claims.get("auth").toString().split(",")) + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + + // UserDetails 객체를 만들어서 Authentication 리턴 + UserDetails principal = new User(claims.getSubject(), "", authorities); + return new UsernamePasswordAuthenticationToken(principal, "", authorities); + } + + // 토큰 정보 검증 + public boolean validateToken(String token) { + if(token != null) + { + try { + Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token); + return true; + } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) { + log.info("잘못된 JWT 서명입니다.", e); + } catch (ExpiredJwtException e) { + log.info("만료된 JWT 토큰입니다.", e); + } catch (UnsupportedJwtException e) { + log.info("지원되지 않는 JWT 토큰입니다.", e); + } catch (IllegalArgumentException e) { + log.info("JWT 토큰이 잘못되었습니다.", e); + } + } + + return false; + } + + private Claims parseClaims(String accessToken) { + try { + return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(accessToken).getBody(); + } catch (ExpiredJwtException e) { + return e.getClaims(); + } + } + +} \ No newline at end of file