Postman调用Spring REST API异常排查及JWT授权密钥疑问
问题排查与JWT令牌使用疑问
我用Postman发起根据ID查询单个用户的GET请求,返回状态码200但无数据,数据库中明明存在对应数据。曾怀疑是antMatchers配置问题但未解决,控制台也无日志输出。其他控制器均可正常访问,仅UserController存在异常。
奇怪现象:在Authorization头中使用refreshKey替代tokenkey时请求可正常工作,请问我可以保留这种方式还是必须使用tokenkey?
SecurityConfig代码
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private UserDetailsServiceImpl userDetailsService; private JwtAuthenticationEntryPoint handler; public SecurityConfig(UserDetailsServiceImpl userDetailsService, JwtAuthenticationEntryPoint handler) { this.userDetailsService = userDetailsService; this.handler = handler; } /*@Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); }*/ @Bean(BeanIds.AUTHENTICATION_MANAGER) @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOriginPattern("*"); config.addAllowedHeader("*"); config.addAllowedMethod("OPTIONS"); config.addAllowedMethod("HEAD"); config.addAllowedMethod("GET"); config.addAllowedMethod("PUT"); config.addAllowedMethod("POST"); config.addAllowedMethod("DELETE"); config.addAllowedMethod("PATCH"); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } @Override public void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity .cors() .and() .csrf().disable() .exceptionHandling().authenticationEntryPoint(handler).and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .authorizeRequests() .antMatchers(HttpMethod.GET, "/type") .permitAll() .antMatchers(HttpMethod.GET, "/nation") .permitAll() .antMatchers(HttpMethod.GET, "/recept") .permitAll() .antMatchers(HttpMethod.GET, "/recept/**") .permitAll() .antMatchers(HttpMethod.GET, "/ingredient") .permitAll() .antMatchers(HttpMethod.GET, "/recept/{\\d+}") .permitAll() .antMatchers("/users/**") .permitAll() .antMatchers("/auth/**") .permitAll() .anyRequest().authenticated(); httpSecurity.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } }
UserController代码
@Slf4j @RestController public class UserController { private UserService userService; public UserController(UserService userService) { this.userService = userService; } @RequestMapping(value="/users",method= RequestMethod.GET, headers = "Accept=application/json") public List<UserResponse> getAllUsers(){ return userService.getAllUsers().stream().map(u -> new UserResponse(u)).collect(Collectors.toList()); } @RequestMapping(value="/users",method= RequestMethod.POST, headers = "Accept=application/json") public ResponseEntity<Void> createUser(@RequestBody User newUser) { User user = userService.saveOneUser(newUser); if(user != null) return new ResponseEntity<>(HttpStatus.CREATED); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } @RequestMapping(value="/users/{userId}",method= RequestMethod.GET, headers = "Accept=application/json") public UserResponse getOneUser(@PathVariable Long userId) { log.info (String.valueOf (userId)); User user = userService.getOneUserById(userId); if(user == null) { throw new UserNotFoundException (); } return new UserResponse(user); } @RequestMapping(value="/users/{userId}",method= RequestMethod.PUT, headers = "Accept=application/json") public ResponseEntity<Void> updateOneUser(@PathVariable Long userId, @RequestBody User newUser) { User user = userService.updateOneUser(userId, newUser); if(user != null) return new ResponseEntity<>(HttpStatus.OK); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } @RequestMapping(value="/users/{userId}",method= RequestMethod.DELETE, headers = "Accept=application/json") public void deleteOneUser(@PathVariable Long userId) { userService.deleteById(userId); } }
UserResponse代码
@Data public class UserResponse { Long id; int avatarId; String userName; public UserResponse(User entity) { this.id = entity.getId(); this.avatarId = entity.getAvatar(); this.userName = entity.getUser_name (); } }
JwtAuthenticationEntryPoint代码
@Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage()); } }
JwtAuthenticationFilter代码
public class JwtAuthenticationFilter extends OncePerRequestFilter{ @Autowired JwtTokenProvider jwtTokenProvider; @Autowired UserDetailsServiceImpl userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { String jwtToken = extractJwtFromRequest(request); if(StringUtils.hasText(jwtToken) && jwtTokenProvider.validateToken(jwtToken)) { Long id = jwtTokenProvider.getUserIdFromJwt(jwtToken); UserDetails user = userDetailsService.loadUserById(id); if(user != null) { UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(auth); } } } catch(Exception e) { return; } filterChain.doFilter(request, response); } private String extractJwtFromRequest(HttpServletRequest request) { String bearer = request.getHeader("Authorization"); if(StringUtils.hasText(bearer) && bearer.startsWith("Bearer ")) return bearer.substring("Bearer".length() + 1); return null; } }
问题根源与解决方案
空数据返回原因
你的JwtAuthenticationFilter中,捕获异常后直接return,没有执行filterChain.doFilter(request, response),导致请求根本没走到UserController的getOneUser方法,自然返回空内容且无日志输出。而用refreshKey时,token验证通过,过滤器正常放行,请求能到达控制器并查询到数据。refreshKey代替tokenkey的风险
绝对不能这么做:- refreshToken的设计目的是获取新的AccessToken,不是用来做接口认证的,它的有效期通常更长,一旦泄露,攻击者可长期获取权限,风险极大。
- 你的
JwtTokenProvider没有区分AccessToken和RefreshToken的验证逻辑,这违反JWT最佳实践。
修复步骤
- 修正过滤器异常处理:捕获异常后,对
permitAll的接口要继续放行,不需要认证的接口不能被拦截try { // 原有逻辑 } catch(Exception e) { log.error("JWT认证失败", e); // 仅拦截需要认证的接口 if (!new AntPathMatcher().match("/users/**", request.getRequestURI())) { handler.commence(request, response, new AuthenticationException("JWT认证失败") {}); return; } } filterChain.doFilter(request, response); - 区分AccessToken和RefreshToken:生成时用不同签名密钥,或在JWT的claim中标记token类型,验证时校验类型,确保RefreshToken只能用于刷新令牌
- 检查日志配置:确保
UserController的日志级别为INFO及以下,保证日志能输出到控制台 - 调试查询逻辑:在
userService.getOneUserById中添加日志,确认传入的userId和数据库查询结果是否匹配
- 修正过滤器异常处理:捕获异常后,对
内容的提问来源于stack exchange,提问作者Water Baloon
相关产品推荐
相关产品推荐

