Skip to content

Commit

Permalink
feat: Add JwtTokenProvider (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
youjin09222 committed Nov 10, 2023
1 parent cedae93 commit aceda13
Showing 1 changed file with 113 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<? extends GrantedAuthority> 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();
}
}

}

0 comments on commit aceda13

Please sign in to comment.