【问题标题】:401 instead of 403 with Spring Boot 2401 而不是 403 与 Spring Boot 2
【发布时间】:2018-08-20 20:04:26
【问题描述】:

使用 Spring Boot 1.5.6.RELEASE 我能够发送 HTTP 状态代码 401 而不是 403,如 How let spring security response unauthorized(http 401 code) if requesting uri without authentication 中所述,这样做:

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //...
        http.exceptionHandling()
                .authenticationEntryPoint(new Http401AuthenticationEntryPoint("myHeader"));
        //...
    }
}

使用org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint 类。

我刚刚升级到Spring Boot 2.0.0.RELEASE,发现不再有这样的类(至少在那个包中)。

问题:

  • 这个类 (Http401AuthenticationEntryPoint) 在 Spring Boot 中是否存在?

  • 如果不是,那么在现有项目中保持相同行为以保持与依赖此状态码 (401) 而不是 403 的其他实现的一致性的好选择是什么?

【问题讨论】:

    标签: java spring spring-boot spring-security http-status-code-401


    【解决方案1】:

    注意

    默认情况下当 spring-boot-starter-security 作为依赖项添加并执行未经授权的请求时,Spring Boot 2 将返回 401。

    如果您放置一些自定义配置来修改安全机制行为,这可能会改变。如果是这种情况,并且您确实需要强制设置401 状态,请阅读以下原帖。

    原帖

    类 org.springframework.boot.autoconfigure.security.Http401AuthenticationEntryPoint 被删除以支持 org.springframework.security.web.authentication.HttpStatusEntryPoint。

    在我的情况下,代码如下所示:

    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //...
            http.exceptionHandling()
                .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
            //...
        }
    }
    

    奖金

    如果您需要在响应正文中返回一些信息或以某种方式自定义响应,您可以执行以下操作:

    1- 扩展AuthenticationEntryPoint

    public class MyEntryPoint implements AuthenticationEntryPoint {
        private final HttpStatus httpStatus;
        private final Object responseBody;
    
        public MyEntryPoint(HttpStatus httpStatus, Object responseBody) {
            Assert.notNull(httpStatus, "httpStatus cannot be null");
            Assert.notNull(responseBody, "responseBody cannot be null");
            this.httpStatus = httpStatus;
            this.responseBody = responseBody;
        }
    
        @Override
        public final void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
            response.setStatus(httpStatus.value());
    
            try (PrintWriter writer = response.getWriter()) {
                writer.print(new ObjectMapper().writeValueAsString(responseBody));
            }
        }
    }
    

    2- 为安全配置提供MyEntryPoint 的实例

    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // customize your response body as needed
            Map<String, String> responseBody = new HashMap<>();
            responseBody.put("error", "unauthorized");
    
            //...
            http.exceptionHandling()
                .authenticationEntryPoint(new MyEntryPoint(HttpStatus.UNAUTHORIZED, responseBody));
            //...
        }
    }
    

    【讨论】:

    • 现在错误的凭据请求返回 401,但响应为空。此外,应该返回 403 的未经授权的请求也会返回 401 和空体响应。
    • 这会返回没有正文的 401。这对 js 来说很好,但是当在 Firefox 中查看时,它是一个空白页面,在 Chrome 中查看时,它会显示“此页面无法正常工作”。不过,很容易自己替换 Htp401AuthenticationEntryPoint 并使用它。只需实现 AuthenticationEntryPoint 并设置您想要的任何状态和消息。
    • @Planky 非常感谢您指出这一点!我只是根据您的评论提出了其他人可能会遵循的方法:)
    【解决方案2】:

    只是为了详细说明@lealceldeiro 的答案:

    在 Spring Boot 2 之前,我的 Securiy Configuration 类看起来像这样:

    @Configuration
    public class MyConfig extends WebSecurityConfigurerAdapter {
    
        @Bean
        public Http401AuthenticationEntryPoint securityException401EntryPoint() {
          return new Http401AuthenticationEntryPoint("Bearer realm=\"webrealm\"");
        }
    
        @Autowired
        private Http401AuthenticationEntryPoint authEntrypoint;
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    
          // some http configuration ...
    
          // Spring Boot 1.5.x style
          http.exceptionHandling().authenticationEntryPoint(authEntrypoint);
        }
    //...
    }
    

    现在在 Spring Boot 2 中它看起来像这样:

    @Configuration
    public class MyConfig extends WebSecurityConfigurerAdapter {
    
        //Bean configuration for Http401AuthenticationEntryPoint can be removed
    
        //Autowiring also removed
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    
          // some http configuration ...
    
          // Spring Boot 2 style
          http.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
        }
    //...
    }
    

    在 Spring Boot Github Repo 中也可以查看 comment > PR 删除 Http401AuthenticationEntryPoint。

    【讨论】:

    • 该解决方案看起来不错且干净,但它缺少强制性的WWW-Authenticate Header,它将告诉客户端他如何尝试进行身份验证
    【解决方案3】:

    Http401AuthenticationEntryPoint 已被删除。

    请参阅 Spring Boot Github 存储库 > 问题 #10715(删除 Http401AuthenticationEntryPoint):

    移除 Http401AuthenticationEntryPoint

    rwinch 于 2017 年 10 月 20 日发表评论
    据我所知,它没有在 Spring Boot 代码库中使用,因此删除 Http401AuthenticationEntryPoint 可能会很好。

    根据您的要求,您可以使用:

    【讨论】:

    • 谢谢,来自 spring boot git repo 的非常有用的链接。在我提供的答案中,未来的读者可以看到我如何根据自己的要求使用HttpStatusEntryPoint。
    【解决方案4】:

    您可以通过覆盖 AuthenticationEntryPoint 类来自定义您的逻辑 这应该可以工作:

    @Component public class AuthEntryPointException implements AuthenticationEntryPoint, Serializable {
    
        private static final long serialVersionUID = -8970718410437077606L;
    
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException {
            response.setStatus(HttpStatus.SC_UNAUTHORIZED);
            response.setContentType("application/json");
            response.getWriter().write("{\"result\":\"UNAUTHORIZED\",\"message\":\"UNAUTHORIZED or Invalid Token\"}");
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-09-04
      • 2015-08-19
      • 2015-08-16
      • 2021-11-25
      • 2016-04-27
      • 2021-08-26
      • 2018-11-01
      • 2022-09-27
      • 2021-01-28
      相关资源
      最近更新 更多