[ERROR] @SpringBootTest @WithMockUser with OAuth2 and JWT tokens TokenAuthenticationFilter error

반응형

1. 문제

Spring Security + OAuth2를 사용해 JWT Token 기반 로그인을 구현한 경우, @WithMockUser를 사용해 Autenticated Rest API에 접근시 TokenAuthenticationFilter에서 error가 발생한다.

@WithMockUser(username="USER_NAME")

    @Test
    @WithMockUser(username = "USER_NAME")
    public void user_me_유저정보조회_성공() throws Exception {
        // given
        UserPrincipal userPrincipal = UserPrincipal.create(user);

        // when
        mvc.perform(
            get("/user/me")
            .contentType(MediaType.APPLICATION_JSON_UTF8)
            .content(new Gson().toJson(userPrincipal)))
        .andDo(print())
        .andExpect(status().isOk());
    }

2. 원인

@WithMockUser는 아래와 같이 username/password 정보만을 가지고 있기 때문에, TokenAuthenticaiton Filter에서 필요한 id 값이 들어있지 않다.

@WithMockUser

public @interface WithMockUser {

    String value() default "user";
    String username() default "";
    String[] roles() default { "USER" };
    String[] authorities() default {};
    String password() default "password";
}

TokenAuthenticationFilter doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain)

try {
     String jwt = getJwtFromRequest(request);

     if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
         Long userId = tokenProvider.getUserIdFromToken(jwt);

         UserDetails userDetails = customUserDetailsService.loadUserById(userId);
         UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
         authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

         SecurityContextHolder.getContext().setAuthentication(authentication);
     }
 } catch (Exception ex) {
     logger.error("Could not set user authentication in security context", ex);
 }

 filterChain.doFilter(request, response);

현재 내가 구현해놓은 JWT Token은 Authenticated Rest API에 접근시, JWT Token에서 id를 decode해 UserDetail 정보를 load 하고있다.

3. 해결

MockMvc에 UserDetail을 전달한다. 이때 UserDetail은 @SpringBootTest의 setup( )에서 저장한 테스트용 User를 사용하면 된다.

MockMvc.with(user(UserDetail))

@Before
public void setup(){

    // mvc
    mvc = MockMvcBuilders
            .webAppContextSetup(context)
            .apply(springSecurity())
            .build();

    // save user
    user = userRepository.save(UserSetup.builder().build().get());
}

@Test
public void user_me_유저정보조회_성공() throws Exception {
    // given
    UserPrincipal userPrincipal = UserPrincipal.create(user);

    // when
    mvc.perform(
        get("/user/me")
        .with(user(userPrincipal)))
    .andDo(print())
    .andExpect(status().isOk());
}

위의 UserPrincipal은 UserDetail을 implements한 custom 구현체이다.

UserPrincipal

public class UserPrincipal implements OAuth2User, UserDetails  {

...
}

반응형

댓글

Designed by JB FACTORY