使用 @PreAuthorize 时从 Spring 控制器返回 405 与 403

Posted

技术标签:

【中文标题】使用 @PreAuthorize 时从 Spring 控制器返回 405 与 403【英文标题】:405 vs 403 returned from Spring Controllers when using @PreAuthorize 【发布时间】:2014-07-30 01:09:29 【问题描述】:

我们最近开始将 @PreAuthorize 注释与我们的 REST 端点一起使用。它工作得很好,但是,我确实对发出 GET 与 POST 或 PUT 时返回的 HTTP 代码有疑问。 当用户无权访问控制器的 REST 端点时,GET 和 PUT/POST 返回的 HTTP 状态似乎不同。

例如,如果我有一个 GET 端点并且有一个 @PreAuthorize 注释并且用户没有访问权限,则返回 403 Forbidden。这是我所期望的。

如果随后将相同的注释放置在 POST 或 PUT 的控制器方法上,则 HTTP 响应为 405 Method Not Allowed(请注意,正确授权后,POST/PUT 方法按预期返回 200)。

单步执行代码时,您可以看到底层安全过滤器返回 403,但在 POST/PUT 场景中,状态代码被丢弃/忽略并替换为 405,就像发生 NullPointerExcpetion 时一样在您的控制器代码中。

这是预期的行为还是应该始终为无权访问端点的用户返回 403 Forbidden?

【问题讨论】:

在我看来,web security layer 抛出了 405,而不是 @PreAuthorize 注释的访问控制层。尝试为包 org.springframework.security 启用调试级别,看看那里到底发生了什么 我认为您的请求方法类型不正确 【参考方案1】:

对我来说,问题在于SecurityConfiguration

通过删除.and().exceptionHandling().accessDeniedPage("/access-denied") 行,我得到了403 Forbidden 而不是405 Method not allowed,这很可能是您所期望的。

【讨论】:

你知道为什么指定自定义访问被拒绝页面会改变行为吗? 我没有对此进行调查,抱歉【参考方案2】:

有可能在请求之间的某处被内部重定向并最终到达一个期望不同 HTTP 请求类型的端点,因此您得到一个 HTTP 405。 我已经看到这在身份验证的情况下是最常见的原因

【讨论】:

【参考方案3】:

我通过在我的错误控制器中允许额外的 HTTP 方法解决了这个问题,而不是只允许 GET 请求:

public class MyErrorController implements ErrorController 
    @RequestMapping(method = 
            RequestMethod.GET,
            RequestMethod.HEAD,
            RequestMethod.POST,
            RequestMethod.PUT,
            RequestMethod.DELETE,
            RequestMethod.OPTIONS,
            RequestMethod.TRACE,
            RequestMethod.PATCH)
    public String displayError()
        // display error
    

我的解释:

    请求的资源允许使用 POST 方法,但 SpringSecurity 由于身份验证或授权无效而拒绝该请求。 SpringSecurity 在内部重定向到错误控制器以显示 HTTP 403 的错误页面。此处使用初始请求中的 HTTP 方法,即 POST。 错误控制器只允许 GET 请求,这就是返回 HTTP 405 的原因。

【讨论】:

以上是关于使用 @PreAuthorize 时从 Spring 控制器返回 405 与 403的主要内容,如果未能解决你的问题,请参考以下文章

Spring自定义授权无法使用@PreAuthorize

使用@PreAuthorize 时的安全配置

使用 JUnit 测试具有 @PreAuthorize 的服务方法

使用自定义 bean 测试 PreAuthorize 注释

spring @Preauthorize 中的自定义方法

@PreAuthorize 并按 ID 删除