java 为什么客户端在成功请求时会出现403错误- Spring?

8e2ybdfx  于 2023-04-28  发布在  Java
关注(0)|答案(1)|浏览(312)

我正在玩Sping Boot ,我已经到了尝试管理安全性的地步。我遵循过时的教程,所以有很多我必须改进自己,但我已经到了我的请求工作正常的地步-需要以前的登录,然后在头中CSRF令牌,他们是否应该做日志记录状态,如200/201,但也有400/404(所有自定义)。问题是,只有当我阅读日志(或通过检查是否创建/删除某些内容来验证结果)时,我才知道它,因为我收到的响应通常是403。我说通常,因为我在修复其他东西的时候,不知何故设法修复了POST-〉201(所以我甚至不知道如何修复)。
下面是我的配置:

@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
        delegate.setCsrfRequestAttributeName("_csrf");
        CsrfTokenRequestHandler requestHandler = delegate::handle;
        http.cors().and().csrf().ignoringRequestMatchers("/api/login").csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).csrfTokenRequestHandler(requestHandler)
            .and()
                .authorizeHttpRequests()
                    .requestMatchers("/sayhello").permitAll()
                    .requestMatchers("/api/**").authenticated()
            .and()
                .httpBasic()
            .and()
                .exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint)
            .and()
                .formLogin().loginProcessingUrl("/api/login")
                    .successHandler(loginSuccessHandler)
                    .failureHandler(new SimpleUrlAuthenticationFailureHandler());
        return http.build();
    }

“失败”请求示例:

curl --location --request DELETE 'http://<xxx>/api/todo/40ae6186' \
--header 'X-XSRF-TOKEN: <xxx>' \
--header 'Cookie: JSESSIONID=<xxx>; XSRF-TOKEN=<xxx>'

如果有一个对象要删除(应该发送200)或者没有(404),这并不重要-客户端得到的响应是403。我试过在控制台和 Postman 。
如果重要的话,这里是来自容器的日志:未找到:

2023-04-25T00:10:10.269Z DEBUG 1 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.ditidev.demospring.controller.ToDoController#deleteById(String)
2023-04-25T00:10:10.269Z DEBUG 1 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.ditidev.demospring.controller.ToDoController#deleteById(String)
2023-04-25T00:10:10.270Z DEBUG 1 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.ditidev.demospring.controller.ToDoController#deleteById(String)
2023-04-25T00:10:10.270Z DEBUG 1 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.ditidev.demospring.controller.ToDoController#deleteById(String)
2023-04-25T00:10:10.271Z DEBUG 1 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : DELETE "/api/todo/2c08f95b", parameters={}
2023-04-25T00:10:10.271Z DEBUG 1 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.ditidev.demospring.controller.ToDoController#deleteById(String)
2023-04-25T00:10:10.275Z DEBUG 1 --- [nio-8080-exec-3] org.hibernate.SQL                        : select t1_0.id,t1_0.completed,t1_0.title from to_do t1_0 where t1_0.id=?
2023-04-25T00:10:10.285Z DEBUG 1 --- [nio-8080-exec-3] .w.s.m.a.ResponseStatusExceptionResolver : Resolved [com.ditidev.demospring.exception.EntityNotFoundException]
2023-04-25T00:10:10.285Z DEBUG 1 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet        : Completed 404 NOT_FOUND
2023-04-25T00:10:10.286Z DEBUG 1 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2023-04-25T00:10:10.287Z DEBUG 1 --- [nio-8080-exec-3] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)

二百:

2023-04-25T00:10:38.548Z DEBUG 1 --- [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.ditidev.demospring.controller.ToDoController#deleteById(String)
2023-04-25T00:10:38.549Z DEBUG 1 --- [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.ditidev.demospring.controller.ToDoController#deleteById(String)
2023-04-25T00:10:38.550Z DEBUG 1 --- [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.ditidev.demospring.controller.ToDoController#deleteById(String)
2023-04-25T00:10:38.551Z DEBUG 1 --- [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.ditidev.demospring.controller.ToDoController#deleteById(String)
2023-04-25T00:10:38.551Z DEBUG 1 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet        : DELETE "/api/todo/40ae6186", parameters={}
2023-04-25T00:10:38.551Z DEBUG 1 --- [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.ditidev.demospring.controller.ToDoController#deleteById(String)
2023-04-25T00:10:38.555Z DEBUG 1 --- [nio-8080-exec-4] org.hibernate.SQL                        : select t1_0.id,t1_0.completed,t1_0.title from to_do t1_0 where t1_0.id=?
2023-04-25T00:10:38.562Z DEBUG 1 --- [nio-8080-exec-4] org.hibernate.SQL                        : delete from to_do where id=?
2023-04-25T00:10:38.576Z DEBUG 1 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet        : Completed 200 OK
2023-04-25T00:10:38.578Z DEBUG 1 --- [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2023-04-25T00:10:38.579Z DEBUG 1 --- [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)

我最担心的是重复的 BasicErrorController#error 消息,所以我试着跟踪,但没有任何进展。我还运行了远程调试,事实上有时它确实向客户端发送了正确的响应,但我无法确定是什么导致了变化。通常同时发生的只有一件事-- Java错误:

2023-04-25T00:21:57.403Z ERROR 1 --- [nio-8080-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] threw exception

org.springframework.security.access.AccessDeniedException: Access Denied
        at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:98) ~[spring-security-web-6.0.2.jar!/:6.0.2]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.2.jar!/:6.0.2]
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) ~[spring-security-web-6.0.2.jar!/:6.0.2]
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) ~[spring-security-web-6.0.2.jar!/:6.0.2]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.2.jar!/:6.0.2]

2023-04-25T00:21:57.404Z ERROR 1 --- [nio-8080-exec-8] o.a.c.c.C.[Tomcat].[localhost]           : Exception Processing ErrorPage[errorCode=0, location=/error]

jakarta.servlet.ServletException: Unable to handle the Spring Security Exception because the response is already committed.
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:144) ~[spring-security-web-6.0.2.jar!/:6.0.2]
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:120) ~[spring-security-web-6.0.2.jar!/:6.0.2]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.2.jar!/:6.0.2]
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:100) ~[spring-security-web-6.0.2.jar!/:6.0.2]
...
Caused by: org.springframework.security.access.AccessDeniedException: Access Denied
        at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:98) ~[spring-security-web-6.0.2.jar!/:6.0.2]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.0.2.jar!/:6.0.2]
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126) ~[spring-security-web-6.0.2.jar!/:6.0.2]

编辑26.04。2023我做了更多的调试,只有在完全删除安全配置后,我才意识到我的一些请求抛出了一些意想不到的东西,但乍一看是隐藏的(响应状态没有改变,所以我没有查看日志,假设一切正常)。

2023-04-26T00:01:38.289Z DEBUG 1 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet        : DELETE "/api/todo/7e999648-3cbc-49d0-b698-2a7a2733a644", parameters={}
2023-04-26T00:01:38.290Z DEBUG 1 --- [nio-8080-exec-7] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.ditidev.demospring.controller.ToDoController#deleteById(String)
2023-04-26T00:01:38.296Z DEBUG 1 --- [nio-8080-exec-7] org.hibernate.SQL                        : select t1_0.id,t1_0.completed,t1_0.title from to_do t1_0 where t1_0.id=?
2023-04-26T00:01:38.383Z DEBUG 1 --- [nio-8080-exec-7] org.hibernate.SQL                        : delete from to_do where id=?
2023-04-26T00:01:38.494Z DEBUG 1 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet        : Completed 200 OK
2023-04-26T00:01:38.495Z DEBUG 1 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet        : "ERROR" dispatch for DELETE "/error", parameters={}
2023-04-26T00:01:38.497Z DEBUG 1 --- [nio-8080-exec-7] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
2023-04-26T00:01:38.498Z DEBUG 1 --- [nio-8080-exec-7] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Using 'application/json', given [*/*] and supported [application/json, application/*+json]
2023-04-26T00:01:38.499Z DEBUG 1 --- [nio-8080-exec-7] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Writing [{timestamp=Wed Apr 26 00:01:38 UTC 2023, status=200, error=OK, message=Succesfully deleted., path=/a (truncated)...]
2023-04-26T00:01:38.500Z DEBUG 1 --- [nio-8080-exec-7] o.s.web.servlet.DispatcherServlet        : Exiting from "ERROR" dispatch, status 200

现在,虽然还没有完全的信心,我可以说,最有可能的403是由安全过滤器发送的,由于这个意外的部分错误调度。没有启用安全性的响应状态只是保持不变,我一定错过了它之前。现在,还有一件更重要的事情--这只会发生在DELETE请求中(无论结果如何)。

另一个编辑看起来其中一个原因(有两个)是为状态添加“原因”

@ResponseStatus(code=HttpStatus.OK, reason="Succesfully deleted.")

这有什么问题最初我让我的delete方法不返回任何东西,这导致了缺乏反馈,所以我添加了'reason'来拥有一些东西。现在我将object设置为返回(或String),它不会抛出任何意外的错误,但我仍然需要看看void会发生什么。
关于第二个原因-这是自定义异常,但在这里我似乎无法理解什么是错误的,因为它看起来像这样:

@ResponseStatus(HttpStatus.NOT_FOUND)
public class EntityNotFoundException extends RuntimeException {}

现在这是有问题的,但它必须是因为这个异常也被GET方法在找不到对象时使用。我的另一个想法是,也许自定义响应需要ResponseBody注解,但当响应是来自DB的对象时,为什么不需要它呢?以下是我的方法来回顾:

@GetMapping
public List<ToDo> findAll(){
    return toDoService.findAll();
}

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ToDo create(@RequestBody ToDo toDo){
    return toDoService.save(toDo);
}

@DeleteMapping("/{id}")
@ResponseStatus(code=HttpStatus.OK, reason="Succesfully deleted.")
public ToDo deleteById(@PathVariable String id){
    return toDoService.deleteById(id);
}

PS我仍然需要验证修复我发现的是否是我最初问题的答案,但显然我也需要理解根本原因。

ubof19bj

ubof19bj1#

所以经过大量的测试,我现在确定了这个问题。似乎403是每个异常的默认错误代码,由CSRF保护的安全抛出。这很可能是默认行为,甚至可能是有意为之。这样的回应不包含任何有用的东西-既不是真实的状态代码,也不是原因。为什么成功的请求会发生在我身上?这有点棘手,但似乎每当@ResponseStatus包含“reason”(空时为ignoerd)时,Spring都会假设这是不必要行为的原因,并默认抛出错误。这个错误是安全性阻止响应并发送403的标志。

* <p><strong>Warning</strong>: when using this annotation on an exception
 * class, or when setting the {@code reason} attribute of this annotation,
 * the {@code HttpServletResponse.sendError} method will be used.

这不是很令人满意(异常处理,而不是响应状态原因),因为我可能必须找到一种方法来改变这种行为,至少在开发环境中,因为它使测试更容易。我看到了一些关于AccessDeniedHandler的东西,但我需要更多的研究。对于那些将面临类似问题的人来说,这仍然是一个提示。

相关问题