我已经在我的spring云api网关服务器上启用了CSRF。我使用angular作为GUI框架,它通过api网关调用其余服务。
我使用了自定义过滤器将CSRF令牌添加到响应标头。
当进行开机自检调用时,我看到表单数据丢失。所以我总是收到 400 个错误请求错误。
我禁用了CSRF,请求正常,没有任何问题。
有什么问题吗?
下面是我的spring云网关配置。网关仅用于将请求路由到其他微服务,它没有任何控制器或Restendpoint。
@SpringBootApplication
public class GatewayApplication {
@Autowired
ProfileManager profileManager;
@PostConstruct
public void onInit() {
profileManager.printActiveProfiles();
}
public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); }
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http.authorizeExchange().anyExchange().permitAll();
http.csrf().csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse());
return http.build();
}
}
下面是过滤器代码
@Component
public class CsrfHeaderFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
Mono<CsrfToken> token = (Mono<CsrfToken>) exchange.getAttributes().get(CsrfToken.class.getName());
if (token != null) {
return token.flatMap(t -> chain.filter(exchange));
}
return chain.filter(exchange);
}
}
我的 POST Restendpoint定义为
@RequestParam
下面是来自rest服务endpoint之一的代码。它是使用传统servlet springboot框架实现的上游服务。
@RequestMapping(value = "terminate/{listName}", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED)
@CrossOrigin
@Loggable (activityname = ActivityLogConstants.DESCRIPTOR_TERMINATE)
public Response terminate(@Context HttpServletRequest reqContext, @PathVariable String listName, @RequestParam(value = "rowData") String rowData)
throws ServiceException {....}
当请求到达上游服务时,formData 将丢失。
看起来Spring云网关中的过滤器正在阻止formData
以下是我的网络配置:
@Configuration
public class NettyConfiguration implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
@Value("${server.max-initial-line-length:65536}")
private int maxInitialLingLength;
@Value("${server.max-http-header-size:65536}")
private int maxHttpHeaderSize;
public void customize(NettyReactiveWebServerFactory container) {
container.addServerCustomizers(
httpServer -> httpServer.httpRequestDecoder(
httpRequestDecoderSpec -> {
httpRequestDecoderSpec.maxHeaderSize(maxHttpHeaderSize);
httpRequestDecoderSpec.maxInitialLineLength(maxInitialLingLength);
return httpRequestDecoderSpec;
}
)
);
}
}
示例日志:
2022-07-28 09:18:20.743 调试 26532 --- [ctor-http-nio-5] r.n.http.client.HttpClientOperations : [id:199cd714-5, L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080] 收到的响应(自动读取:false):[X-Content-Type-Options=nosniff, X-XSS-Protection=1; mode=block, Cache-Control=no-cache, no-store, max-age=0, must-revalidate, Pragma=no-cache, Expires=0, Strict-Transport-Security=max-age=31536000 ; includeSubDomain, X-Frame-Options=DENY, X-Application-Context=application:18080, 日期=星期四, 28 七月 2022 03:48:20 GMT, 连接=关闭, 内容长度=0] 2022-07-28 09:18:20.744 调试 26532 --- [ctor-http-nio-5] r.n.r.DefaultPooledConnectionProvider : [id:199cd714-5, L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080] onStateChange(POST{uri=/cms-service/webapi/terminate/descriptor, connection=PooledConnection{channel=[id: 0x199cd714, L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080]}}, [response_received]) 2022-07-28 09:18:20.744 调试 26532 --- [ctor-http-nio-5] reactor.netty.channel.FluxReceive : [id:199cd714-5, L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080] FluxReceive{pending=0, cancel=false, inboundDone=false, inboundError=null}: 订阅入站接收器 2022-07-28 09:18:20.744 调试 26532 --- [ctor-http-nio-5] r.n.http.client.HttpClientOperations : [id:199cd714-5, L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080] 收到最后一个 HTTP 数据包 2022-07-28 09:18:20.744 调试 26532 --- [ctor-http-nio-5]r.n.http.server.HttpServerOperations : [id:b0f975eb-11, L:/0:0:0:0:0:0:0:1:10443 - R:/0:0:0:0:0:0:1:50337] 减少挂起的响应,现在为 0 2022-07-28 09:18:20.745 调试 26532 --- [ctor-http-nio-5] r.n.http.server.HttpServerOperations : [id:b0f975eb-11, L:/0:0:0:0:0:0:1:10443 - R:/0:0:0:0:0:0:0:1:50337] 上次发送 HTTP 数据包,终止通道 2022-07-28 09:18:20.745 调试 26532 --- [ctor-http-nio-5] o.s.w.s.adapter.HttpWebHandlerAdapter : [b0f975eb-11, L:/0:0:0:0:0:0:0:1:10443 - R:/0:0:0:0:0:0:0:1:50337] 已完成 400 BAD_REQUEST 2022-07-28 09:18:20.745 调试 26532 --- [ctor-http-nio-5] r.n.http.server.HttpServerOperations : [id:b0f975eb-11, L:/0:0:0:0:0:0:1:10443 - R:/0:0:0:0:0:0:0:1:50337] 最后一个 HTTP 响应帧 2022-07-28 09:18:20.745 调试 26532 --- [ctor-http-nio-5] c.m.webgateway.handler.RequestLogger : 处理 /cms-service/webapi/terminate/descriptor 请求所需的总时间 60055 2022-07-28 09:18:20.745 调试 26532 --- [ctor-http-nio-5] r.n.r.DefaultPooledConnectionProvider : [id:199cd714, L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080] onStateChange(POST{uri=/cms-service/webapi/terminate/descriptor, connection=PooledConnection{channel=[id: 0x199cd714, L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080]}}, [response_completed]) 2022-07-28 09:18:20.745 调试 26532 --- [ctor-http-nio-5] r.n.r.DefaultPooledConnectionProvider : [id:199cd714, L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080] onStateChange(POST{uri=/cms-service/webapi/terminate/descriptor, connection=PooledConnection{channel=[id: 0x199cd714, L:/127.0.0.1:50342 - R:localhost/127.0.0.1:18080]}}, [断开连接]) 2022-07-28 09:18:20.752 调试 26532 --- [ctor-http-nio-5] r.n.resources.PooledConnectionProvider : [id:199cd714, L:/127.0.0.1:50342 !R:localhost/127.0.0.1:18080] 通道已关闭,现在:0 个活动连接、4 个非活动连接和 0 个挂起的获取请求。2022-07-28 09:18:20.752 调试 26532 --- [ctor-http-nio-5] r.n.r.DefaultPooledConnectionProvider : [id:199cd714, L:/127.0.0.1:50342 !R:localhost/127.0.0.1:18080] onStateChange(PooledConnection{channel=[id: 0x199cd714, L:/127.0.0.1:50342 !R:localhost/127.0.0.1:18080]}, [断开连接]) 2022-07-28 09:18:23.805 调试 26532 --- [ctor-http-nio-5] r.n.http.server.HttpServerOperations : [id:b0f975eb, L:/0:0:0:0:0:0:0:1:10443 - R:/0:0:0:0:0:0:0:1:50337] 增加待处理响应,现在 1 2022-07-28 09:18:23.805 调试 26532 --- [ctor-http-nio-5] reactor.netty.http.server.HttpServer : [id:b0f975eb-12, L:/0:0:0:0:0:0:0:0:1:10443 - R:/0:0:0:0:0:0:0:1:50337] 正在应用处理程序:org.springframework.http.server.reactive。ReactorHttpHandlerAdapter@7c82616c 2022-07-28 09:18:23.805 调试 26532 --- [ctor-http-nio-5] o.s.w.s.adapter.HttpWebHandlerAdapter : [b0f975eb-12, L:/0:0:0:0:0:0:0:1:10443 - R:/0:0:0:0:0:0:0:1:50337] HTTP GET “/cms-service/webapi/data/descriptor”
下面是示例项目的链接。https://github.com/manjosh1990/webgateway-issues
我试图忽略表单URL编码的请求和GET请求,但它仍然不起作用
private static final Set<HttpMethod> ALLOWED_METHODS = new HashSet<>(
Arrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.TRACE, HttpMethod.OPTIONS));
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange().anyExchange().permitAll().and()
.csrf(csrf -> csrf
.requireCsrfProtectionMatcher(ignoringFormUrlEncodedContentType())
.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()));
return http.build();
}
private ServerWebExchangeMatcher ignoringFormUrlEncodedContentType() {
return (exchange) -> !MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(
exchange.getRequest().getHeaders().getContentType()) || !ALLOWED_METHODS.contains(exchange.getRequest().getMethod())
? ServerWebExchangeMatcher.MatchResult.match()
: ServerWebExchangeMatcher.MatchResult.notMatch();
}
感谢您提供最小的样本来重现问题!
经过一些测试,我无法想出一个解决方法或修复您的配置,允许表单发布(URL编码)在启用CSRF保护的情况下通过网关。我的最佳猜测是,它与Spring Security如何使用请求正文(应该缓存以供后续过滤器使用)以及Spring Cloud Gateway如何使用请求正文以代理到下游服务有关。
我通过禁用CSRF保护并添加以下过滤器对此进行了测试:
@Component
public class TestWebFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return Mono.defer(() -> exchange.getFormData()
.doOnSuccess(System.out::println))
.then(chain.filter(exchange));
}
}
在我的测试中,这会导致通过网关的请求在收到之前长时间阻塞:
{
"timestamp": "2022-08-10T19:13:54.265+00:00",
"status": 400,
"error": "Bad Request",
"path": "/cms-service/webapi/service/post/test"
}
由于这似乎是Spring Security中的一个bug,我建议提交一个Spring Security中的bug,我们可以从那里着手解决它。
如果您想同时解决此问题,您可以为这些类型的请求禁用CSRF保护,如下所示:
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange((authorize) -> authorize
.anyExchange().authenticated()
)
.csrf((csrf) -> csrf
.requireCsrfProtectionMatcher(ignoringFormUrlEncodedContentType())
.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())
)
.oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::jwt);
return http.build();
}
private ServerWebExchangeMatcher ignoringFormUrlEncodedContentType() {
return (exchange) -> !MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(
exchange.getRequest().getHeaders().getContentType())
? ServerWebExchangeMatcher.MatchResult.match()
: ServerWebExchangeMatcher.MatchResult.notMatch();
}
}
重要提示:这并不理想,因为这些请求不会受到保护。但是,如果这些请求从未在浏览器中执行过,这可能是有意义的。在这种情况下,有一个单独的身份验证机制是有意义的,比如要求一个不记名令牌而不是表单登录,等等。(如上例)。
我正在为一个项目使用Hackerrank API。查看官方文档,点击这里! 在他们的网站上有一个使用UNIREST的例子, 由于我使用的是axios,所以我将其转换为类似的axios代码,如下所示: 我希望这只适用于示例中所示的示例,但它给我带来了以下错误: 请求失败,状态代码为400 错误:请求失败,状态代码为400 在createError(createError.js:16) 在sett(s
我尝试发送一个删除请求到我的服务器,但我得到一个400坏的请求错误代码。我找了很多,但我找不到任何有帮助解决方案。当我尝试与邮差,请求工作良好。 这是来自Postman的curl命令的外观: 这是我的Java代码: 我还尝试在头中不设置Content-Type,但当我得到一个415错误时,我还删除了getBodyContentType()方法,但这也没有改变。 还有什么能帮上忙的点子吗?
我收到 400 个错误的 AJAX 发布方法请求。我正在后端使用Spring数据Rest服务。以下是我在JS前端的代码 尽管我正在序列化 JSON 数据。我仍然收到400错误请求错误。如果后端的某些代码中断或发送到服务器的请求出现问题,则会出现此错误吗? JAVA实现
但如果我将请求更改为“multipart/form-data”-春回错误400“bad request”: 为什么?也许我应该创造一些额外的豆子? PS:我需要“multipart/form-data”来与json对象一起发送文件。
我有一个基于Spring Web model view controller(MVC)框架的项目。Spring Web模型-视图-控制器(MVC)框架的版本是3.2.8 我有这个控制器 这个URL一切正常:
目前从Angular JS controller中,我试图将JSON数据发送到后端服务。但是我有400个错误的请求错误。 在Controller中,我试图通过http服务发送数据,如下所示: