코드 그라데이션

토큰 API 구현하기 본문

SpringBoot [예제] 블로그 만들기/시큐리티, JWT 입히기

토큰 API 구현하기

완벽한 장면 2023. 10. 19. 17:25

토큰 API 구현하기

- 여기서는 리프레시 토큰을 전달방ㄷ아 검증하고,

  유효한 리프레시 토큰이라면 새로운 액세스 토큰을 생성하는 토큰 API를 구현한다.

- 토큰 서비스, 컨트롤러를 차례대로 구현한다.

 

1. 토큰 서비스 추가하기

- 리프레시 토큰을 전달받아 토큰 제공자를 사용해 새로운 액세스 토큰을 만드는 클래스

1-1. UserService 에 findById() 추가하기

@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;

    public Long save(AddUserRequest dto) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

        // UserRepository를 통해 새 사용자를 저장.
        // - AddUserRequest에서 전달된 이메일과 비밀번호를 사용하여 새 사용자를 생성.
        // - 비밀번호는 BCryptPasswordEncoder를 사용하여 안전하게 해싱됨
        return userRepository.save(User.builder()
                .email(dto.getEmail())
                .password(encoder.encode(dto.getPassword()))
                .build()).getId(); // 저장된 사용자의 ID를 반환
    }



 //추가
    public User findById(Long userId) {
        return userRepository.findById(userId)
                .orElseThrow(() -> new IllegalArgumentException("Unexpected user"));
    }

}

 

1-2. RefreshTokenService에 findByRefreshToken() 메서드 구현

@RequiredArgsConstructor
@Service
public class RefreshTokenService {

    private final RefreshTokenRepository refreshTokenRepository;

    // 리프레시 토큰 조회 메서드
    public RefreshToken findByRefreshToken(String refreshToken) {
        return refreshTokenRepository.findByRefreshToken(refreshToken)
                .orElseThrow(() -> new IllegalArgumentException("Unexpected token"));
    }
}

 

2. 컨트롤러 추가하기

2-1. DTO 추가하기

1) CreateAccessTokenRequest

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

 

2) CreateAccessTokenResponse

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

 

2-2. 컨트롤러 추가 - TokenApiController

@RestController
@RequiredArgsConstructor
public class TokenApiController {

    private final TokenService tokenService;

    @PostMapping("/api/token")
    public ResponseEntity<CreateAccessTokenResponse> createNewAccessToken
            (@RequestBody CreateAccessTokenRequest request) {

        // TokenService를 사용하여 새로운 액세스 토큰 생성을 요청
        String newAccessToken
                = tokenService.createNewAccessToken(request.getRefreshToken());

        // 생성된 액세스 토큰을 ResponseEntity로 래핑하여 반환
        // CreateAccessTokenResponse 객체는 생성된 액세스 토큰을 포함하는 응답 데이터이다.
        return ResponseEntity.status(HttpStatus.CREATED)
                .body(new CreateAccessTokenResponse(newAccessToken));
    }
}

 

2-3. 테스트 추가

개요

@SpringBootTest
@AutoConfigureMockMvc
class TokenApiControllerTest {

    @Autowired
    protected MockMvc mockMvc;

    @Autowired
    protected ObjectMapper objectMapper;
    @Autowired
    JwtProperties jwtProperties;
    @Autowired
    UserRepository userRepository;
    @Autowired
    RefreshTokenRepository refreshTokenRepository;
    @Autowired
    private WebApplicationContext context;

    @BeforeEach
    public void mockMvcSetUp() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
                .build();
        userRepository.deleteAll();
    }

    @DisplayName("createNewAccessToken: 새로운 액세스 토큰을 발급한다.")
    @Test
    public void createNewAccessToken() throws Exception {
        // given
        final String url = "/api/token";

        User testUser = userRepository.save(User.builder()
                .email("user@gmail.com")
                .password("test")
                .build());

        String refreshToekn = JwtFactory.builder()
                .claims(Map.of("id", testUser.getId()))
                .build()
                .createToken(jwtProperties);

        refreshTokenRepository.save(new RefreshToken(testUser.getId(), refreshToekn));

        CreateAccessTokenRequest request = new CreateAccessTokenRequest();
        request.setRefreshToken(refreshToekn);
        final String requestBody = objectMapper.writeValueAsString(request);

        // when
        ResultActions resultActions = mockMvc.perform(post(url)
                .contentType(MediaType.APPLICATION_JSON_VALUE)
                .content(requestBody));

        // then
        resultActions
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.accessToken").isNotEmpty());
    }
}

 

 

 

728x90
Comments