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

[Spring Security] Refresh Token ๊ธฐ๋ฐ˜์œผ๋กœ new AccessToken๋ฐ›๋Š” API ๊ตฌํ˜„ํ•˜๊ธฐ

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

 

๋‚ด๊ฐ€ ์˜ˆ์ „ ํฌ์ŠคํŒ…์—์„œ ์ป๋˜ ํ† ํฐ ์ธ์ฆ ๊ณผ์ •์ž„.

 


[๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ๋ฐœ๊ธ‰ ๊ณผ์ •]

โ€‹

1. ํด๋ผ์ด์–ธํŠธ -> ์„œ๋ฒ„ : ์ธ์ฆ ์š”์ฒญ

2. ์„œ๋ฒ„ -> ํด๋ผ์ด์–ธํŠธ : ์•ก์„ธ์Šค ํ† ํฐ & ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ์‘๋‹ต

3. ์„œ๋ฒ„ -> DB : ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์€ ์ €์žฅ

4. ํด๋ผ์ด์–ธํŠธ -> ์„œ๋ฒ„ :API ์š”์ฒญ

5. ์„œ๋ฒ„ : ํ† ํฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํ•˜๊ณ  ์‘๋‹ต

~~ ์‹œ๊ฐ„์ด ํ๋ฅด๊ณ  ~~

6. ํด๋ผ์ด์–ธํŠธ -> ์„œ๋ฒ„ : (๋งŒ๋ฃŒ๋œ ํ† ํฐ์œผ๋กœ) API ์š”์ฒญ

7. ์„œ๋ฒ„ -> ํด๋ผ์ด์–ธํŠธ : ํ† ํฐ ๋งŒ๋ฃŒ๋๋‹ค๊ณ  ์‘๋‹ต

8. ํด๋ผ์ด์–ธํŠธ -> ์„œ๋ฒ„ : (๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ๊ณผ ํ•จ๊ผ) ์•ก์„ธ์Šค ํ† ํฐ ๋ฐœ๊ธ‰ ์š”์ฒญ

9. ์„œ๋ฒ„ -> DB : ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ์กฐํšŒ & ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ

10. ์„œ๋ฒ„ -> ํด๋ผ : ๋งŒ์กฑํ•˜๋ฉด, new ์•ก์„ธ์Šค ํ† ํฐ์œผ๋กœ ์‘๋‹ต

11. ํด๋ผ -> ์„œ๋ฒ„ : new ์•ก์„ธ์Šค ํ† ํฐ์œผ๋กœ ๋‹ค์‹œ ์š”์ฒญ...(4๋ฒˆ๋ถ€ํ„ฐ ๋ฐ˜๋ณต)

 

 

 

๐Ÿ’ก ์•ก์„ธ์Šค ํ† ํฐ์ด ์œ ํšจํ•  ๋•Œ

JWT ์ž์ฒด์— ์žˆ๋Š” Claim์—์„œ userId๊ฐ™์€ ์ •๋ณด ๊บผ๋‚ด์˜ด

DB ์ ‘๊ทผ ์•ˆํ•ด๋„ ๋จ.

 

๐Ÿ’ก ์•ก์„ธ์Šค ํ† ํฐ์ด ๋งŒ๋ฃŒ๋์„ ๋•Œ

๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ๋ฐ›์•„์„œ

DB์—์„œ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ์กฐํšŒ, ํ•ด๋‹น ํ† ํฐ์— ๊ด€๋ จ๋œ userId์–ป์–ด์„œ

์ƒˆ ์—‘์„ธ์Šค ํ† ํฐ ๋งŒ๋“ฆ

 

 

 

 

 

 

 

 


์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š”,

๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ๋ฐ›์•„์„œ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌํ•˜๊ณ 

์œ ํšจํ•œ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์ด๋ผ๋ฉด ์ƒˆ๋กœ์šด accessToken๋ฐœ๊ธ‰ํ•˜๋Š”

ํ† ํฐ API๋งŒ๋“ค๊ฑฐ์ž„!

 

 

 

 

๋จผ์ € UserService.java์ชฝ์— ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•จ

 

@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    public Long save(AddUserRequestDto dto) {
        return userRepository.save(User.builder()
                .email(dto.getEmail())
                .password(bCryptPasswordEncoder.encode(dto.getPassword()))
                .build()).getId();
    }

	// ์ถ”๊ฐ€ ๋œ ๋ฉ”์„œ๋“œ
    public User findById(Long userId) {
        return userRepository.findById(userId)
                .orElseThrow( () -> new IllegalArgumentException("Unexpected user"));
    }
}

 

ํ† ํฐ ๊ธฐ๋ฐ˜ ์š”์ฒญ์ด ๋“ค์–ด์˜จ ํ›„์—

์‹ค์ œ ์œ ์ €๋ฅผ DB์—์„œ ํ™•์ธํ•˜๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ฌ๋•Œ ์‚ฌ์šฉ๋จ.

 

 

 


 

๋‹ค์Œ์œผ๋กœ service๋””๋ ‰ํ† ๋ฆฌ์— RefreshTokenService.java ์ƒˆ ํŒŒ์ผ ์ƒ์„ฑํ•ด์„œ

 

@RequiredArgsConstructor
@Service
public class RefreshTokenService {

    private final RefreshTokenRepository refreshTokenRepository;

    public RefreshToken findByRefreshToken(String refreshToken) {
        return refreshTokenRepository.findByRefreshToken(refreshToken)
                .orElseThrow( () -> new IllegalArgumentException("Unexpected refresh token"));
    }
}

 

์ด ๊ณผ์ •์€, ์œ„์— ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ๋ฐœ๊ธ‰๊ณผ์ •

(๋…ธ๋ž€์ƒ‰ ํ˜•๊ด‘ํŽœ)

์ค‘ 9๋ฒˆ์— ํ•ด๋‹นํ•˜๋Š” ์ฝ”๋“œ์ž„!

 

 

 


 

service ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด์—

TokenService.java ํŒŒ์ผ ์ƒ์„ฑ

 

@RequiredArgsConstructor
@Service
public class TokenService {

    private final TokenProvider tokenProvider;
    private final RefreshTokenService refreshTokenService;
    private final UserService userService;

    public String createNewAccessToken(String refreshToken) {

        // ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
        if(!tokenProvider.validToken(refreshToken)) {
            throw new IllegalArgumentException("Invalid refresh token");
        }
        
        Long userId = refreshTokenService.findByRefreshToken(refreshToken).getUserId();
        User user = userService.findById(userId);
        
        return tokenProvider.generateToken(user, Duration.ofHours(2));
    }
}

 

 

์—ฌ๊ธฐ์„œ ์ž‘์„ฑํ•œ createNewAccessToken()๋ฉ”์„œ๋“œ๋Š”

๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ์ „๋‹ฌ๋ฐ›๊ณ 

 

์œ ํšจ์„ฑ ๊ฒ€์‚ฌํ•˜๊ณ 

์œ ํšจ๋‹ค๋ฉด ๊ทธ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ๊ธฐ๋ฐ˜์œผ๋กœ userId์ฐพ๊ณ 

๊ทธ ์œ ์ €ํ•œํ…Œ ์ƒˆ๋กœ์šด accessToken์„

tokenProvider.generateToken()ํ•ด์„œ ๋„˜๊ฒจ์คŒ.

 

 

 

 


 

์ปจํŠธ๋กค๋Ÿฌ ์ชฝ ๋“ค์–ด๊ฐ€๊ธฐ ์ „์— DTO๋“ค๋ถ€ํ„ฐ ๋งŒ๋“ค๊ฒ ์Œ.

 

DTO๋‘๊ฐœ ๋งŒ๋“ค๊ณ ~

@Getter
@Setter
public class CreateAccessTokenRequestDto {
    private String refreshToken;
}



@AllArgsConstructor
@Getter
public class CreateAccessTokenResponseDto {
    private String accessToken;
}

 

 

 

 

 

 

์ปจํŠธ๋กค๋Ÿฌ ์ถ”๊ฐ€ํ•  ์ฐจ๋ก€

@RequiredArgsConstructor
@RestController
public class TokenApiController {
    
    private final TokenService tokenService;
    
    @PostMapping("/api/token")
    public ResponseEntity<CreateAccessTokenResponseDto> createNewAccessToken(
            @RequestBody CreateAccessTokenRequestDto request) {
        String newAccessToken = tokenService.createNewAccessToken(request.getRefreshToken());
        
        return ResponseEntity.status(HttpStatus.CREATED)
                .body(new CreateAccessTokenResponseDto(newAccessToken));
    }
}

 

๊ธฐ์กด ์•ก์„ธ์Šค ํ† ํฐ์ด ๋งŒ๋ฃŒ๋˜์–ด์„œ

๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ๋ณด๋‚ด์„œ new ์—‘์„ธ์Šค ํ† ํฐ ์ฃผ์„ธ์š”~

ํ• ๋•Œ ๋ณด๋‚ด๋Š” ์—”๋“œ ํฌ์ธํŠธ.

 

 

 

 

 

 

 

๋ฐ˜์‘ํ˜•