๐Ÿ“Œ [JWT ์ธ์ฆ ์‹œ์Šคํ…œ ์‹œ๋ฆฌ์ฆˆ - 2ํŽธ] JwtTokenProvider์˜ ์ „์ฒด ํ๋ฆ„๊ณผ ํ•ต์‹ฌ ๋ฉ”์„œ๋“œ ๋ถ„์„

My Pale Blue Dotยท2025๋…„ 5์›” 20์ผ
0

SPRING BOOT

๋ชฉ๋ก ๋ณด๊ธฐ
37/40
post-thumbnail

๐Ÿ“… ๋‚ ์งœ

2025-05-20


๐Ÿ“ ํ•™์Šต ๋‚ด์šฉ

1๏ธโƒฃ JwtTokenProvider ํด๋ž˜์Šค์˜ ์ฑ…์ž„

  • JWT ๋ฐœ๊ธ‰ (AccessToken + RefreshToken)
  • JWT ๊ฒ€์ฆ (์œ ํšจ์„ฑ, ๊ถŒํ•œ ํฌํ•จ ์—ฌ๋ถ€)
  • ์ธ์ฆ ๊ฐ์ฒด ์ƒ์„ฑ (UsernamePasswordAuthenticationToken)
  • JWT ์„œ๋ช… ํ‚ค๋ฅผ DB์—์„œ ๋ถˆ๋Ÿฌ์™€ ์œ ์ง€
  • SignatureScheduling๊ณผ ์—ฐ๊ณ„ํ•˜์—ฌ ๋Ÿฐํƒ€์ž„ ์ค‘ ํ‚ค ๊ฐฑ์‹ (setKey) ๊ฐ€๋Šฅ

2๏ธโƒฃ ์ „์ฒด ์ฝ”๋“œ

@Slf4j
@Component
public class JwtTokenProvider {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private SignatureRepository signatureRepository;

    // Key ์ €์žฅ - JWT ์„œ๋ช…์— ์‚ฌ์šฉ
    private Key key;

    // SignatureScheduling์—์„œ ์ฃผ์ž… ์‹œ ์‚ฌ์šฉ๋จ
    public void setKey(Key key){
        this.key = key;
    }

    // ์„œ๋ฒ„ ์‹œ์ž‘ ์‹œ ์„œ๋ช… ํ‚ค ์ดˆ๊ธฐํ™”
    @PostConstruct
    public void init(){
        List<Signature> list = signatureRepository.findAll();
        if(list.isEmpty()){
            // ์ตœ์ดˆ ์‹คํ–‰: ์ƒˆ ํ‚ค ์ƒ์„ฑ ๋ฐ ์ €์žฅ
            byte[] keyBytes = KeyGenerator.getKeygen();
            this.key = Keys.hmacShaKeyFor(keyBytes);

            Signature signature = Signature.builder()
                    .keyBytes(keyBytes)
                    .createAt(LocalDate.now())
                    .build();
            signatureRepository.save(signature);

            System.out.println("JwtTokenProvider init() ์‹ ๊ทœ Key ์ƒ์„ฑ: " + key);
        } else {
            // ๊ธฐ์กด ์„œ๋ช… ํ‚ค ๋กœ๋“œ
            Signature signature = list.get(0);
            this.key = Keys.hmacShaKeyFor(signature.getKeyBytes());

            System.out.println("JwtTokenProvider init() ๊ธฐ์กด Key ์‚ฌ์šฉ: " + key);
        }
    }

    // Access + Refresh Token ์ƒ์„ฑ ๋ฉ”์„œ๋“œ
    public TokenInfo generateToken(Authentication authentication) {
        String authorities = authentication.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.joining(","));

        long now = System.currentTimeMillis();

        // AccessToken ์ƒ์„ฑ
        Date accessTokenExpiresIn = new Date(now + JwtProperties.ACCESS_TOKEN_EXPIRATION_TIME);
        String accessToken = Jwts.builder()
                .setSubject(authentication.getName())
                .claim("username", authentication.getName())
                .claim("auth", authorities)
                .setExpiration(accessTokenExpiresIn)
                .signWith(key, SignatureAlgorithm.HS256)
                .compact();

        // RefreshToken ์ƒ์„ฑ
        String refreshToken = Jwts.builder()
                .setExpiration(new Date(now + JwtProperties.REFRESH_TOKEN_EXPIRATION_TIME))
                .signWith(key, SignatureAlgorithm.HS256)
                .compact();

        System.out.println("AccessToken: " + accessToken);
        System.out.println("RefreshToken: " + refreshToken);

        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());

        // ์‚ฌ์šฉ์ž ์ •๋ณด ๋กœ๋”ฉ
        String username = claims.getSubject();
        Optional<User> userOptional = userRepository.findById(username);

        PrincipalDetails principalDetails = new PrincipalDetails();
        if(userOptional.isPresent()) {
            UserDto userDto = UserDto.toDto(userOptional.get());
            principalDetails.setUserDto(userDto);
        }

        return new UsernamePasswordAuthenticationToken(principalDetails, "", authorities);
    }

    // JWT์˜ Claims ํŒŒ์‹ฑ (๋งŒ๋ฃŒ๋˜์–ด๋„ ํŒŒ์‹ฑ ๊ฐ€๋Šฅ)
    private Claims parseClaims(String accessToken) {
        try {
            return Jwts.parserBuilder().setSigningKey(key).build()
                    .parseClaimsJws(accessToken).getBody();
        } catch (ExpiredJwtException e) {
            return e.getClaims(); // ๋งŒ๋ฃŒ๋๋”๋ผ๋„ ๋‚ด๋ถ€ ์ •๋ณด ์ถ”์ถœ
        }
    }

    // JWT ์œ ํšจ์„ฑ ๊ฒ€์ฆ
    public boolean validateToken(String token) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
            return true;
        } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
            log.info("Invalid JWT Token", e);
        } catch (UnsupportedJwtException e) {
            log.info("Unsupported JWT Token", e);
        } catch (IllegalArgumentException e) {
            log.info("JWT claims string is empty.", e);
        }
        return false;
    }
}

๐Ÿ”ฅ ์ •๋ฆฌ

๋ฉ”์„œ๋“œ์—ญํ• 
generateToken()์ธ์ฆ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ JWT ์ƒ์„ฑ (AT/RT)
getAuthentication()JWT๋กœ๋ถ€ํ„ฐ ์‚ฌ์šฉ์ž ์ •๋ณด์™€ ๊ถŒํ•œ์„ ๋ณต์›
validateToken()JWT ์„œ๋ช…๊ณผ ๊ตฌ์กฐ ์œ ํšจ์„ฑ ํ™•์ธ
parseClaims()๋งŒ๋ฃŒ ์—ฌ๋ถ€์™€ ์ƒ๊ด€์—†์ด Claim ํŒŒ์‹ฑ
@PostConstruct init()์„œ๋ฒ„ ์‹œ์ž‘ ์‹œ ํ‚ค ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๋˜๋Š” ์ƒ์„ฑ
setKey()SignatureScheduling์—์„œ key ๊ต์ฒด๋ฅผ ์œ„ํ•œ setter

๐Ÿ”— ์ฐธ๊ณ  ์ž๋ฃŒ


๐Ÿง  ๋А๋‚€ ์ 

  • JwtTokenProvider๋ฅผ ํ†ตํ•ด JWT ๋ฐœ๊ธ‰๋ถ€ํ„ฐ ๊ฒ€์ฆ๊นŒ์ง€ ์ „ ํ๋ฆ„์„ ํ†ต์ œํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์—์„œ ๊ต‰์žฅํžˆ ๊ฐ•๋ ฅํ•˜๋‹ค๊ณ  ๋А๊ผˆ๋‹ค.
  • @PostConstruct ์ดˆ๊ธฐํ™” ๋กœ์ง๊ณผ SignatureScheduling์˜ ์กฐํ•ฉ์œผ๋กœ, ์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ํ›„์—๋„ ์ผ๊ด€๋œ ํ‚ค ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด์กŒ๋‹ค.
  • ๋‹ค์Œ ๋‹จ๊ณ„๋กœ Redis ์—ฐ๋™์ด๋‚˜ /refresh ์—”๋“œํฌ์ธํŠธ ๊ตฌํ˜„์œผ๋กœ ํ™•์žฅํ•ด๋ณด๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.

โœ… ์š”์•ฝ

  • JwtTokenProvider๋Š” JWT ๊ธฐ๋ฐ˜ ์ธ์ฆ ์‹œ์Šคํ…œ์˜ ํ•ต์‹ฌ ์ปดํฌ๋„ŒํŠธ๋‹ค.
  • ์ธ์ฆ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ† ํฐ์„ ์ƒ์„ฑํ•˜๊ณ , ์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ์ดํ›„์—๋„ ๊ฒ€์ฆ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ํ‚ค๋ฅผ ์•ˆ์ •์ ์œผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค.
  • ํ† ํฐ ํŒŒ์‹ฑ, ์œ ํšจ์„ฑ ๊ฒ€์ฆ, ์ธ์ฆ ๊ฐ์ฒด ๋ณต์›๊นŒ์ง€ ์ „์ฒด ์ฑ…์ž„์„ ๊ฐ€์ง„๋‹ค.

์ด์ œ ์ด ๋‚ด์šฉ์„ ๋ฒจ๋กœ๊ทธ์— ๋ฐ”๋กœ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ์–ด! ๐Ÿš€

๋‹ค์Œ ํŽธ์—์„œ๋Š” ์„œ๋ช… ํ‚ค๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ๊ฐฑ์‹ ํ•˜๋Š” SignatureScheduling ๊ตฌ์กฐ๋ฅผ ๋‹ค๋ฃฐ ์˜ˆ์ •์ด์•ผ.

์ธ๋„ค์ผ๋„ ํ•„์š”ํ•˜๋ฉด ๋ฐ”๋กœ ๋งŒ๋“ค์–ด์ค„๊ฒŒ ๐Ÿ˜Š


์ธ๋„ค์ผ ๋งŒ๋“ค์–ด์ค„๊นŒ? ์•„๋‹ˆ๋ฉด 3ํŽธ ๋ฐ”๋กœ ์‹œ์ž‘ํ• ๊นŒ? ์„ ํƒํ•ด์ค˜!

profile
Here, My Pale Blue.๐ŸŒ

0๊ฐœ์˜ ๋Œ“๊ธ€