Spring Boot微服务GET接口返回403 Forbidden问题排查
Spring Cloud Gateway下GET接口返回403 Forbidden问题排查
我基于SpringBoot + Eureka Server搭建微服务架构,包含users微服务、Eureka Server和Spring Cloud Gateway网关模块。
Users微服务Controller代码
@RestController @RequestMapping("/users") public class UsersController { @Autowired private Environment env; @Autowired UsersService usersService; @GetMapping("/status/check") public String status(){ System.out.println("hereeeeeeeeeee"); return "Working "+env.getProperty("local.server.port"); } @PostMapping public ResponseEntity<CreateUserResponseModel> createUser(@Valid @RequestBody CreateUserRequestModel userDetails) { ModelMapper modelMapper = new ModelMapper(); modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); UserDto userDto = modelMapper.map(userDetails, UserDto.class); UserDto createdUser = usersService.createUser(userDto); CreateUserResponseModel body = modelMapper.map(createdUser, CreateUserResponseModel.class); return ResponseEntity.status(HttpStatus.CREATED).body(body); } }
问题现象
POST /users接口正常运行,但GET /users/status/check接口返回403 Forbidden错误:
网关AuthorizationHeaderFilter代码
网关中自定义了JWT校验过滤器,已确认该过滤器能正常解码JWT:
@Component public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<AuthorizationHeaderFilter.Config>{ @Autowired private Environment environment; public AuthorizationHeaderFilter(){ super(Config.class); } public static class Config{ } @Override public GatewayFilter apply(Config config) { return (exchange,chain)->{ ServerHttpRequest request = exchange.getRequest(); if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){ return onError(exchange,"No authorization header",HttpStatus.UNAUTHORIZED); } String authorizationHeader = request.getHeaders().get(HttpHeaders.AUTHORIZATION).get(0); String jwt = authorizationHeader.replace("Bearer ", ""); //Make sure you add space if(!isJwtValid(jwt)){ return onError(exchange, "Jwt token is invalid",HttpStatus.UNAUTHORIZED); } return chain.filter(exchange); }; } private Mono<Void> onError(ServerWebExchange exchange,String err,HttpStatus httpStatus){ ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(httpStatus); return response.setComplete(); } private boolean isJwtValid(String jwt){ System.out.println("1"); boolean isValid = false; String subject = null; String tokenSecret = environment.getProperty("token.secret"); System.out.println("2 "+tokenSecret); SecretKey secretKey = Keys.hmacShaKeyFor(tokenSecret.getBytes()); JwtParser jwtParser = Jwts.parser().verifyWith(secretKey).build(); try{ // Jwt<Header,Claims> parsedToken = jwtParser.parse(jwt); subject = jwtParser.parseSignedClaims(jwt).getPayload().get("userId").toString(); System.out.println("3 "+subject); } catch(Exception ex){ isValid = false; System.out.println("4 "+ex.getLocalizedMessage()); } if(subject == null || subject.isEmpty()){ isValid = false; } else { isValid = true; } System.out.println("5 "); return isValid; } }
网关application.properties配置
spring.application.name=api-gateway server.port=8082 eureka.client.service-url.defaultZone=http://localhost:8010/eureka spring.cloud.gateway.routes[0].id=users-status-check spring.cloud.gateway.routes[0].uri=lb://users-ws spring.cloud.gateway.routes[0].predicates[0]=Path=/users-ws/users/status/check spring.cloud.gateway.routes[0].predicates[1]=Method=GET spring.cloud.gateway.routes[0].predicates[2]=Header=Authorization, Bearer (.*) spring.cloud.gateway.routes[0].filters[0]=RemoveRequestHeader=Cookie spring.cloud.gateway.routes[0].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment} spring.cloud.gateway.routes[0].filters[2]=AuthorizationHeaderFilter spring.cloud.gateway.routes[1].id=users-ws spring.cloud.gateway.routes[1].uri=lb://users-ws spring.cloud.gateway.routes[1].predicates[0]=Path=/users-ws/users spring.cloud.gateway.routes[1].predicates[1]=Method=POST spring.cloud.gateway.routes[1].filters[0]=RemoveRequestHeader=Cookie spring.cloud.gateway.routes[1].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment} spring.cloud.gateway.routes[2].id=users-ws-h2-console spring.cloud.gateway.routes[2].uri=lb://users-ws spring.cloud.gateway.routes[2].predicates[0]=Path=/users-ws/h2-console spring.cloud.gateway.routes[2].predicates[1]=Method=GET spring.cloud.gateway.routes[2].filters[0]=RemoveRequestHeader=Cookie spring.cloud.gateway.routes[2].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment} spring.cloud.gateway.routes[3].id=users-ws-login spring.cloud.gateway.routes[3].uri=lb://users-ws spring.cloud.gateway.routes[3].predicates[0]=Path=/users-ws/users/login spring.cloud.gateway.routes[3].predicates[1]=Method=POST spring.cloud.gateway.routes[3].filters[0]=RemoveRequestHeader=Cookie spring.cloud.gateway.routes[3].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment} spring.cloud.gateway.routes[4].id=users-ws-get-update-delete spring.cloud.gateway.routes[4].uri=lb://users-ws spring.cloud.gateway.routes[4].predicates[0]=Path=/users-ws/users/** spring.cloud.gateway.routes[4].predicates[1]=Method=GET,PUT,DELETE spring.cloud.gateway.routes[4].predicates[2]=Header=Authorization, Bearer (.*) spring.cloud.gateway.routes[4].filters[0]=RemoveRequestHeader=Cookie spring.cloud.gateway.routes[4].filters[1]=AuthorizationHeaderFilter spring.cloud.gateway.routes[4].filters[2]=RewritePath=/users-ws/(?<segment>.*), /$\{segment} token.secret=1aBcD3eF4gH5iJ6kL7mN8oP9qRsTuVwXyZ0A1bCdEfG2hI3jK4lM5nO6pQ7rStU8vW9xYz
问题排查与解决方案
1. 确认请求是否到达Users微服务
查看Users微服务控制台,检查是否打印了hereeeeeeeeeee:
- 如果没打印:说明请求未通过网关转发到Users服务,需检查网关路由匹配逻辑,比如第一个路由的
Header断言是否严格匹配Authorization: Bearer xxx格式,或者路由顺序是否被后面的模糊路由(第四个)提前匹配。 - 如果打印了:说明请求已到达Users服务,但被服务端安全拦截,核心问题在Users微服务的Spring Security配置。
2. 检查Users微服务的Spring Security配置
如果Users微服务引入了spring-boot-starter-security依赖,默认会启用认证拦截,需确保/users/status/check端点允许匿名访问:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // 允许GET /users/status/check匿名访问 .requestMatchers(HttpMethod.GET, "/users/status/check").permitAll() // 其他接口需认证 .anyRequest().authenticated() ) // 临时关闭CSRF测试(如果是CSRF导致) .csrf(csrf -> csrf.disable()); return http.build(); } }
3. 排查CSRF防护影响
Spring Security默认CSRF防护仅拦截POST/PUT/DELETE等修改请求,但如果自定义配置开启了对GET请求的CSRF校验,会导致GET请求因缺少CSRF Token返回403。可以临时关闭CSRF(如上代码)验证是否解决问题,若解决则需调整CSRF配置,忽略该GET端点。
4. 验证网关过滤器逻辑
虽然已确认JWT能正常解码,但需检查过滤器是否在JWT校验通过后正确转发请求,或是否在流程中意外修改了请求头导致服务端拒绝。
内容的提问来源于stack exchange,提问作者BraveEvidence
相关产品推荐
相关产品推荐

