401 而不是 403 Spring Boot 2

401 instead of 403 with Spring Boot 2

使用 Spring Boot 1.5.6.RELEASE I was able to send HTTP Status code 401 instead of 403 as described in ,通过这样做:

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 class。

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

问题:


请注意这与 不同,因为它特指 SpringBoot 2(有解决方案 post 不是Spring启动版本 2 或其他版本不再适用)

注意

默认 Spring Boot 2 将 return 401spring-boot-starter-security 被添加为依赖项并执行了未经授权的请求。

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

原版Post

class 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));
        //...
    }
}

奖金

如果您需要 return 响应正文中的一些信息或以某种方式自定义响应,您可以这样做:

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));
        //...
    }
}

Http401AuthenticationEntryPoint 已删除。

请参阅 Spring 启动 Github Repo > 问题 #10715(删除 Http401AuthenticationEntryPoint):

Remove Http401AuthenticationEntryPoint

rwinch commented on 20 Oct 2017
As far as I can tell it is not being used in the Spring Boot code base, so it might be good to remove Http401AuthenticationEntryPoint.

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

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

在 Spring 引导 2 之前,我的安全配置 class 如下所示:

@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 中的 comment 启动 Github Repo > PR 删除 Http401AuthenticationEntryPoint .

您可以通过覆盖 class 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\"}");
    }
}

对于反应式 (WebFlux) 堆栈,您可以通过添加此类 @Bean 来捕获某些特定异常来覆盖返回的状态代码:

@Component
class MyErrorAttributes : DefaultErrorAttributes() {
override fun getErrorAttributes(
    request: ServerRequest,
    options: ErrorAttributeOptions
): MutableMap<String, Any> {
    val cause = super.getError(request)

    val errorAttributes = super.getErrorAttributes(request, options)

    when (cause) {
        is TokenExpiredException -> {
            errorAttributes["status"] = HttpStatus.UNAUTHORIZED.value()
            errorAttributes["error"] = HttpStatus.UNAUTHORIZED.reasonPhrase
        }
    }

    return errorAttributes
}
}