๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Spring Security

[Spring Security] JWT์™€ Authentication๊ฐ์ฒด (์“ฐ์ž„์˜ ์ฐจ์ด, ๋‚ด๊ฐ€ ํ—ท๊ฐˆ๋ ธ๋˜ ๋ถ€๋ถ„ ์ •๋ฆฌ)

by ์šฐ์ฃผ๋ฌผ๊ณ ๊ธฐ 2025. 5. 4.
๋ฐ˜์‘ํ˜•

๋‚˜๋Š” ์ง€๊ธˆ

JWT ํ† ํฐ ์ƒ์„ฑ + ์œ ํšจ์„ฑ ๊ฒ€์‚ฌํ•˜๋Š” ํด๋ž˜์Šค ๊ณต๋ถ€ํ•˜๋‹ค๊ฐ€

์ƒ๊ธด ๊ถ๊ธˆ์ฆ๋“ค์ด ์žˆ์–ด์„œ

์ •๋ฆฌ ํ•˜๋ ค๊ณ ํ•จ

โ€‹

๊ฑ ๋‚ด๊ฐ€ ์ดํ•ดํ•˜๋ฉด์„œ ์ฃผ์ ˆ์ฃผ์ ˆ ์“ฐ๋Š”๊ฑฐ๋ผ

์ฝ๊ธฐ ์•ˆ์ข‹์„ ์ˆ˜ ์žˆ์Œ..ใ…Ž

โ€‹

 

 

/*
ํ† ํฐ ์ƒ์„ฑ & ์˜ฌ๋ฐ”๋ฅธ ํ† ํฐ์ธ์ง€ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
ํ† ํฐ์—์„œ ํ•„์š”ํ•œ ์ •๋ณด ๊ฐ€์ ธ์˜ค๋Š” ํด๋ž˜์Šค
 */
@RequiredArgsConstructor
@Service
public class TokenProvider {

    private final JwtProperties jwtProperties;

    public String generateToken(User user, Duration expiredAt) {

        Date now = new Date();

        return makeToken(new Date(now.getTime() + expiredAt.toMillis()), user);

    }

    // JWT ํ† ํฐ ์ƒ์„ฑ ๋ฉ”์„œ๋“œ
    private String makeToken(Date expiry, User user) {

        Date now = new Date();

        return Jwts.builder()
                .setHeaderParam(Header.TYPE, Header.JWT_TYPE)
                .setIssuer(jwtProperties.getIssuer())
                .setIssuedAt(now)
                .setExpiration(expiry)
                .setSubject(user.getEmail())
                .claim("id", user.getId())
                .signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
                .compact();
    }

    // JWT ํ† ํฐ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ๋ฉ”์„œ๋“œ
    public boolean validToken(String token) {
        try{
            Jwts.parser().setSigningKey(jwtProperties.getSecretKey())
                    .parseClaimsJws(token);

            return true;
        } catch (Exception e){
            return false;
        }
    }

    // ํ† ํฐ ๊ธฐ๋ฐ˜์œผ๋กœ ์ธ์ฆ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฉ”์„œ๋“œ
    public Authentication getAuthentication(String token) {

        Claims claims = getClaim(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);
    }

    // ํ† ํฐ ๊ธฐ๋ฐ˜์œผ๋กœ ์œ ์ € ID ๊ฐ€์ ธ์˜ค๋Š” ๋ฉ”์„œ๋“œ
    public Long getUserId(String token) {

        Claims claims = getClaim(token);

        return claims.get("id", Long.class);
    }

    private Claims getClaim(String token) {
        return Jwts.parser()
                .setSigningKey(jwtProperties.getSecretKey())
                .parseClaimsJws(token)
                .getBody();
    }
}

 

 

 

์ผ๋‹จ JWT์˜ ์—ญํ• ์€

ํ‹ฐ์ผ“๊ฐ™์€๊ฑฐ์ž„. ์ฆ๋ช…์„œ ์—ญํ• ??

์„œ๋ฒ„์ž…์žฅ์—์„œ๋Š”, '์ด ์œ ์ €๊ฐ€ ์ง„์งœ ๋กœ๊ทธ์ธํ–ˆ๋–ค ๊ทธ ์‚ฌ์šฉ์ž๊ฐ€ ๋งž๊ตฌ๋‚˜'๋ผ๊ณ 

ํ™•์ธํ•˜๋Š” ์ฆ๋ช…์„œ์— ๋ถˆ๊ณผํ•จ.

โ€‹

๋ฐ˜๋ฉด์— Authenticaion๊ฐ์ฒด๋Š”

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ๋‚ด๋ถ€์—์„œ ์“ฐ๋Š”

๋กœ๊ทธ์ธ ์ƒํƒœ ํ‘œํ˜„ ๊ฐ์ฒด ๋ผ๊ณ  ํ•จ.

์„œ๋ฒ„๋Š” ์ด ๊ฐ์ฒด๋ฅผ ๊ธฐ์ค€์œผ๋กœ

'์ง€๊ธˆ ์š”์ฒญ ๋ณด๋‚ธ ์‚ฌ๋žŒ ๋ˆ„๊ตฌ์ง€?'

'์ด ์‚ฌ๋žŒ ๊ถŒํ•œ ์žˆ๋‚˜?'

'๋กœ๊ทธ์ธ ๋˜์–ด์žˆ๋‚˜?'

๋“ฑ๋“ฑ์„ ํŒ๋‹จํ•˜๊ฒŒ ๋จ.

โ€‹

โ€‹

โ€‹

๊ตณ์ด Authentication๊ฐ์ฒด๋ฅผ ์™œ ๋งŒ๋“ค๊นŒ?

JWT์•ˆ์—๋„ ํŽ˜์ด๋กœ๋“œ ๋‚ด์šฉ์ด ์žˆ๋Š”๋ฐ,

๊ทธ๊ฑธ ๋ณด๊ณ  ๋ˆ„๊ตฐ์ง€, ์œ ํšจ๊ธฐ๊ฐ„ ์–ธ์  ์ง€ ๋ณด๋ฉด ๋˜๋Š”๊ฑฐ ์•„๋‹˜?!

๋‚œ ์ด๋Ÿฌ์ผ€ ์ƒ๊ฐํ–ˆ๊ธฐ๋•Œ๋ฌธ์—

Authentication๊ฐ์ฒด์˜ ํ•„์š”์„ฑ์— ๋Œ€ํ•ด์„œ ๊ถ๊ธˆํ–ˆ์Œ.

โ€‹

์ผ๋‹จ ํ•„์š”ํ•œ ์ด์œ 

1. Spring Security ๊ฐ€ ์ž‘๋™ํ•˜๋ ค๋ฉด ํ•„์š”ํ•จ

์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๋Š” ๋‚ด๋ถ€์ ์œผ๋กœ

โ€‹

a. ํด๋ผ์ด์–ธํŠธ๊ฐ€ JWT ๊ฐ–๊ณ  ์š”์ฒญํ•จ

b. ํ•„ํ„ฐ๊ฐ€ JWT๋ฅผ ํŒŒ์‹ฑ -> ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ถ”์ถœํ•จ

c. ๊ทธ๊ฑธ ๋ฐ”ํƒ•์œผ๋กœ Authentication๊ฐ์ฒด ๋งŒ๋“ฆ

d. ๊ทธ๊ฑธ SecurityContextHolder์— ์ €์žฅ

e. ์ดํ›„์— @AuthenticationPrinciple, hasRole ๋“ฑ์—์„œ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋กœ ์ธ์‹.

โ€‹

-> ์ฆ‰, ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋กœ ์ธ์‹ํ•˜๊ณ  ๊ถŒํ•œ ์ฒดํฌํ• ๋ผ๋ฉด

๋ฐ˜๋“œ์‹œ Authentication๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•œ๊ฑฐ์ž„.

โ€‹

โ€‹

2. ์„œ๋ฒ„ ๋‚ด๋ถ€์—์„œ ์ธ์ฆ/์ธ๊ฐ€ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด ํ•„์š”ํ•จ

์˜ˆ๋ฅผ ๋“ค์–ด, ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ํ˜„์žฌ ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด๊ฐ€ ํ•„์š”ํ•  ๋•Œ

 

@GetMapping("/profile")
public ResponseEntity<UserProfile> myProfile(@AuthenticationPrincipal UserDetails userDetails) {
    // ์—ฌ๊ธฐ์„œ userDetails = Authentication ๊ฐ์ฒด ์•ˆ์— ์žˆ๋Š” principal
    return userService.getProfile(userDetails.getUsername());
}

 

์ด๋Ÿฐ์‹์œผ๋กœ ์”€.

์ด๊ฒŒ ๊ฐ€๋Šฅํ•˜๋ ค๋ฉด Authentication๊ฐ์ฒด ๋งŒ๋“ค์–ด์„œ

SecurityContextHolder์— ๋“ฑ๋กํ•ด์•ผ๋จ. ๊ทธ๋ž˜์•ผ

@AuthenticationPrinciple ์–ด๋…ธํ…Œ์ด์…˜ ์“ธ ์ˆ˜ ์žˆ์Œ.

โ€‹

โ€‹

โ€‹

โ€‹

์•„๋ฌดํŠผ JWT๋Š” ๊ทธ๋ƒฅ Stirng ๋ฌธ์ž์—ด, ์ฆ๊ฑฐ์ผ๋ฟ.

Spring Security๋Š” ๊ทธ ํ† ํฐ์„ ๋ณด๊ณ  ํŒŒ์‹ฑํ•ด์„œ

Authentication๊ฐ์ฒด ๋งŒ๋“ค์–ด์„œ ์ €์žฅํ•ด๋‘ฌ์•ผํ•จ.

โ€‹

๊ทธ๊ฑธ ์œ„ํ•œ ๋ฉ”์„œ๋“œ๊ฐ€ getAuthentication()๋ฉ”์„œ๋“œ!

(์ด ๋ฉ”์„œ๋“œ ๋ณด๋ฉด getClaimํ•จ์ˆ˜ ์จ์„œ ๋ญ ๊ฐ€์ ธ์˜ค๊ณ ์žˆ๋Š”๋ฐ,

์ด๊ฒŒ token๋ฐ›์•„์„œ ๊ทธ ์•ˆ์˜ ์ •๋ณด ์ถ”์ถœํ•ด์˜ค๋Š” ๋ถ€๋ถ„์ž„!)

 

๋ฐ˜์‘ํ˜•