You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Spring Cloud Gateway自定义过滤器返回401/403而非500?

解决Spring Gateway过滤器返回401/403而非500的问题

在Spring Gateway中,直接抛出IllegalStateException会被框架判定为内部服务器错误,返回500状态码。要返回符合语义的401或403,你需要主动构建错误响应并终止过滤器链,而非依赖未捕获异常的默认处理逻辑。

以下是修改后的完整代码(同时修正了原代码中先移除Authorization再读取的逻辑错误):

class AuthHeadersFilter(
    private val objectMapper: ObjectMapper
) : AbstractGatewayFilterFactory<AuthHeadersFilter.Config>() {

    override fun apply(config: Config?): GatewayFilter {
        return GatewayFilter { exchange, chain ->
            val httpHeaders = exchange.request.headers
            // 先读取Authorization header,再执行移除操作
            val authHeader = httpHeaders[AUTHORIZATION]?.firstOrNull()
                ?: return@GatewayFilter buildErrorResponse(exchange, HttpStatus.UNAUTHORIZED, "Authorization header is missing")

            // 解析JWT Claims,捕获解析异常返回401
            val jwtClaims = try {
                jwtClaims(authHeader)
            } catch (e: Exception) {
                return@GatewayFilter buildErrorResponse(exchange, HttpStatus.UNAUTHORIZED, "Invalid Authorization token")
            }

            // 检查authz声明是否存在,缺失则返回403
            val authzClaim = jwtClaims.getStringClaimValue("authz")
                ?: return@GatewayFilter buildErrorResponse(exchange, HttpStatus.FORBIDDEN, "Authz claim is missing")

            // 解析权限列表,格式错误则返回403
            val permissions: List<Permissions> = try {
                objectMapper.readValue(authzClaim)
            } catch (e: Exception) {
                return@GatewayFilter buildErrorResponse(exchange, HttpStatus.FORBIDDEN, "Invalid Authz claim format")
            }

            // 构造修改后的请求,移除Authorization并添加X-OrgId
            val modifiedRequest = exchange.request.mutate()
                .headers { headers ->
                    headers.remove(AUTHORIZATION)
                    permissions.forEach {
                        headers.add("X-OrgId", it.scope.org)
                    }
                }.build()

            // 继续执行后续过滤器链
            chain.filter(exchange.mutate().request(modifiedRequest).build())
        }
    }

    // 封装错误响应构建逻辑,返回JSON格式错误信息
    private fun buildErrorResponse(exchange: ServerWebExchange, status: HttpStatus, message: String): Mono<Void> {
        val response = exchange.response
        response.statusCode = status
        response.headers.contentType = MediaType.APPLICATION_JSON

        val errorBody = objectMapper.writeValueAsString(mapOf("error" to message))
        val buffer = response.bufferFactory().wrap(errorBody.toByteArray())

        return response.writeWith(Mono.just(buffer))
    }

    class Config // 保持原配置类定义不变
}

核心修改说明:

  • 替换异常抛出逻辑:将原本的throw IllegalStateException改为调用buildErrorResponse,直接生成指定状态码的响应。
  • 终止过滤器链:通过return@GatewayFilter返回Mono<Void>,告知Spring Gateway不再执行后续的过滤器和路由逻辑。
  • 状态码语义区分:
    • 当Authorization header缺失或Token解析失败时,返回401 Unauthorized(身份验证失败)。
    • 当authz声明缺失或格式错误时,返回403 Forbidden(身份验证通过但无权限)。
  • 修正原代码逻辑bug:调整了Authorization header的读取和移除顺序,避免先移除后读取导致的空值问题。

内容的提问来源于stack exchange,提问作者Rustam Issabekov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 01:48:11