为什么AccessDeniedException和AuthenticationException的处理方式不同

问题描述

我使用Spring Boot 2和Spring Security 5创建了一个演示应用程序。源位于here

它提供了两种端点-HTML网页和REST API。因此,WebSecurityConfigurerAdapter的两个子类被引入如下:

@Configuration
@Order(1)
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.antMatcher("/api/**")
            .authorizeRequests()
            .antMatchers("/api/admin**").hasRole("ADMIN")
            .anyRequest().authenticated()
            .and().httpBasic();
    }
}

@Configuration
public class PageSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/**")
            .authorizeRequests()
            .antMatchers("/login").permitAll()
            .anyRequest().authenticated()
            .and().formLogin().loginPage("/login").permitAll().defaultSuccessUrl("/index")
            .and().logout()
            .invalidateHttpSession(true).deleteCookies("JSESSIONID")
            .logoutSuccessUrl("/login").permitAll();
    }
}

在以下三种情况下,REST api调用将导致身份验证和授权失败:

  1. 未提供受保护端点的凭据
  2. 提供的凭据错误
  3. 正确的凭据提供了错误的角色

结果显示,在情况1和3中org.springframework.security.access.AccessDeniedException上升,在情况2中org.springframework.security.authentication.BadCredentialsException上升,三种情况的返回值为:

  1. HTTP状态为401的json错误消息
  2. HTTP状态302重定向到/ login端点返回
  3. HTTP状态为403的json错误消息

对于情况2,该现象已在另一本question中进行了解释。通常,这是因为异常将由错误端点/error处理,并且在Spring Boot 2中该端点也已受到保护,因此需要登录(详细信息Spring security 5 "Bad credentials" exception not shown with errorDetails)。

进一步的问题是,为什么案例1和案例3没有采用相同的机制处理?错误处理程序为什么不使用AccessDeniedException端点处理/error

解决方法

HTTP.401或»未经身份验证«是因为安全上下文未收到任何凭据。而且,它还取决于所调用的URL(静态页面,角色等)。通常,浏览器会显示一个登录对话框。您的插图不包含此转发案例到登录表单的任何配置。

HTTP.403或»Forbidden«已在链的末端:错误的角色,因此无权访问(无转发,无浏览器对话框)。

您的* SecurityConfig是否驻留在不同的组件/ JAR中?我不这么认为,因为您使用了@Order注释。如果您打算同时激活两个对象,并且它们位于同一JAR中,则将它们合并为一个。这样一来,在不牢记一个或多个另一个的情况下,它的确切含义就更加明显。

我也只能猜测您的角色是如何表达的。至少存在三个不同的选项,例如@RolesAllowed,Spring EL或与您一样。请注意Spring的授予权限和角色之间的区别。如果未进行配置,则后者必须以'ROLE_'开头,否则无法通过hasRole SpringEL找到。其他一切最终都变成了授权机构。

还有一些其他端点可能缺乏保护或现在无法访问,例如监测和健康端点。取决于它们的配置,它可能会对所有端点产生影响(我只是假设可以通过src / main / application.properties或.yaml中的属性来配置它们,而不使用任何@ Configuration-class。)