首页 > 解决方案 > 如何自定义@PreAuthorize("isAuthenticated()") 返回的http状态?

问题描述

在我@PreAuthorize("isAuthenticated()")用来检查用户是否登录的 Spring Boot 控制器中(使用基本身份验证 + JSESSIONID)。
但是如果用户没有登录,即他们没有通过身份验证,控制器会返回403 Forbidden.
根据我对身份验证与授权的了解, 401用于身份验证,而403用于授权。

根据我的理解@PreAuthorize(),总是检查用户的授权(顾名思义),但是有没有办法根据我们传递给它的参数来定制它,即isAuthenticated()这里?

我知道还有另一种解决方案:在 中使用基于 URL 的安全配置configure(HttpSecurity httpSecurity),但如果我不想这样做怎么办。

请有任何想法。

标签: spring-bootspring-security

解决方案


当您使用比 spring 注释方法A@Pre/PostAuthorize时,将为此类对象创建代理,并且对此类方法的每次调用都将通过代理(如果对象在 spring 上下文中)。

的代理@Pre/PostAuthorize将评估来自注释的表达式(这里很复杂),如果它是真的,则传递请求父亲(到真正的方法),如果不是抛出 AccessDaniedException。你也可以在你的方法A中自己抛出这样的异常,效果是一样的。这一切都是关于如何@Pre/PostAuthorize在这里不再有魔法!

但这不是故事的结局!

如果AccessDaniedException没有被抑制或重新翻译(没有没有堆栈的堆栈AccessDaniedException),它将ExceptionTranslationFilter在 Spring Security 中被捕获并ExceptionTranslationFilter查看用户是否已通过身份验证

  • 如果是,ExceptionTranslationFilter则委托AccessDeniedHandler处理这种情况,默认实现AccessDeniedHandler将返回403 http 代码或重定向到错误页面,这取决于您是否使用休息。

  • 如果没有,ExceptionTranslationFilter则委托给AuthenticationEntryPoint它,它将处理这种情况(重定向到登录页面,要求 http 基本 ... 等)。

注意:如果显示500ExceptionTranslationFilter以外的任何异常,将重新AuthenticationException抛出AccessDeniedException异常:(

您的问题可能是另一个问题,请查看Spring Security 匿名 401 而不是 403

抑制:(:

    @GetMapping(value = "/test/ok")
    public String getOk() {
        try {
            myService.securedMethod();
            return "ok";
        } catch (Exception e) {
            log.info("AccessDeniedException suppressed not rethrowing :(", e);
            return "error";
        }
    }

正确重新翻译:):

    @GetMapping(value = "/test/ok")
    public String getOk() {
        try {
            myService.securedMethod();
            return "ok";
        } catch (AccessDeniedException e) {
            throw new MyException(e); //public MyException(Throwable cause)
        }
    }

推荐阅读