WebTestClient+mockJwt时OncePerRequestFilter不触发(Spring Boot2.4.2)
Spring Boot 版本:2.4.2
业务流程
当前应用的完整业务链路如下:
- 外部客户端在
Authorization请求头携带JWT token发起HTTP请求 - 继承
OncePerRequestFilter的JwtRequestFilter从token中解析提取相关信息 - 解析得到的信息传递给
CustomUserDetailsService,校验数据库中是否存在对应用户 - 仅当校验通过时,才会调用控制器方法,解析得到的用户信息通过
@AuthenticationPrincipal注解参数传入方法
测试场景与现存问题
我计划使用Spring提供的WebTestClient搭配mockJwt()对控制器方法开展测试。目前测试请求已经可以触发CustomUserDetailsService的逻辑,但存在两个核心问题:
- 自定义的
JwtRequestFilter完全没有被调用,loadUserById()方法未执行 CustomUserDetailsService无法查询到对应用户:我原本希望通过Mockito模拟仓库层依赖,在测试中保留对真实数据库校验逻辑的验证,但程序始终抛出UsernameNotFoundException
当前测试实现代码
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class UserControllerIT { @Autowired private WebApplicationContext context; private WebTestClient webTestClient; @MockBean private UserRepo userRepo; @BeforeEach public void setup() { webTestClient = MockMvcWebTestClient .bindToApplicationContext(this.context) .apply(springSecurity()) .build(); } @Test @WithUserDetails public void testIt() { final User user = createUser("foo@bar.com", "my-password"); when(userRepo.findByEmail(anyString())).thenReturn(Optional.of(user)); when(userRepo.findById(anyLong())).thenReturn(Optional.of(user)); webTestClient .mutateWith(mockJwt()) .get() .uri("/services/user") .accept(MediaType.APPLICATION_JSON) .exchange() .expectStatus().is2xxSuccessful(); } }
核心实现类代码
JwtRequestFilter
@Component @Slf4j public class JwtRequestFilter extends OncePerRequestFilter { private static final String BEARER_PREFIX = "Bearer "; private final HandlerExceptionResolver resolver; private final JwtService jwtService; private final CustomUserDetailsService userDetailsService; public JwtRequestFilter(@Qualifier("handlerExceptionResolver") HandlerExceptionResolver resolver, JwtService jwtService, CustomUserDetailsService userDetailsService) { this.resolver = resolver; this.jwtService = jwtService; this.userDetailsService = userDetailsService; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { String token = parseTokenFromRequest(request); if (isNotBlank(token)) { String subject = jwtService.getSubject(token); if (isNotBlank(subject) && isNull(SecurityContextHolder.getContext().getAuthentication())) { final MyaUserDetails userDetails = userDetailsService.loadUserById(Long.valueOf(subject)); if (jwtService.validateToken(token, userDetails)) { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } } } filterChain.doFilter(request, response); } catch (JwtException e) { resolver.resolveException(request, response, null, e); } } private String parseTokenFromRequest(HttpServletRequest request) { String token = request.getHeader(HttpHeaders.AUTHORIZATION); if (isNotBlank(token) && token.startsWith(BEARER_PREFIX)) { return token.substring(BEARER_PREFIX.length()); } else { return null; } } }
CustomUserDetailsService
@Service @RequiredArgsConstructor public class CustomUserDetailsService implements UserDetailsService { private static final String USER_NOT_FOUND = "User not found"; private final UserRepo userRepo; @Override public CustomUserDetails loadUserByUsername(String username) throws UsernameNotFoundException { final User user = userRepo.findByEmail(username) .orElseThrow(() -> new UsernameNotFoundException(USER_NOT_FOUND)); return buildUserDetails(user); } public CustomUserDetails loadUserById(Long id) throws UsernameNotFoundException { final User user = userRepo.findById(id) .orElseThrow(() -> new UsernameNotFoundException(USER_NOT_FOUND)); return buildUserDetails(user); } }
UserController
@GetMapping public ResponseEntity<UserData> getUserData(@AuthenticationPrincipal CustomUserDetails userDetails) { try { final UserData userData = userService.getUser(userDetails.getId()); return ResponseEntity.ok(userData); } catch (UserNotFoundException e) { log.error("User with id {} not found", userDetails.getId()); return ResponseEntity.notFound().build(); } }
内容的提问来源于stack exchange,提问作者Robert Strauch
相关产品推荐
相关产品推荐

