添加 'Authorization' header 会导致 Spring 安全性以保护允许的端点

Adding 'Authorization' header causes Spring Security to secure a permitted endpoint

所以,我的 WebSecurityConfigurerAdapter

里有这个
public class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // Use this configuration for endpoints starting with 'api'
                .antMatcher("/api/**")
                // Do not secure endpoints receiving callbacks
                .authorizeRequests().antMatchers(""/api/v1/notification").permitAll()
                // Allow only users with role "ROLE_API"
                .anyRequest().hasRole(Users.UserRoles.ROLE_API.role.replace("ROLE_", ""))
                .and()
                .httpBasic()
                .and()
                // Do not create any sessions
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                // Disable csrf
                .csrf().disable();            

    }

}

不应该保护 /api/v1/notification。如果我在 HTTP header 中调用没有 Authorization: Basic abcd 的端点,则请求是允许的,但如果我添加 Authorization: Basic abcd header,我会得到 401 http响应代码。

注意:Basic abcd 只是随机的,所以我的数据库中没有这样的用户

问题是为什么在 http header 中添加 Authorization... 会使端点再次受到保护?

问得好,这可能有点令人困惑,因为这意味着合法客户端可能会被拒绝密码错误的页面,而世界其他地方无需凭据即可看到该页面。

实际上,这是设计使然。一般来说,授权系统需要先知道用户是谁,然后才能知道用户是否可以进行 X、Y 或 Z 操作。即使使用 public 端点,当用户处于上下文中时端点的行为也可能不同。因此,实际上,它们是首先进行身份验证的独立系统:如果请求提供凭据,则框架将尝试对用户进行身份验证并相应地接受或拒绝请求。

一个选项

我知道你没有问如何解决它(你可能对行为完全满意并且只是好奇),但是你可以用 BasicAuthenticationFilter 做的一件事是将它配置为忽略失败,仅针对该端点:

static class IgnoreFailuresBasicAuthenticationFilter extends BasicAuthenticationFilter {
    private final BasicAuthenticationFilter everythingElse;

    public IgnoreFailuresBasicAuthenticationFilter(BasicAuthenticationFilter everythingElse) {
        super(everythingElse.getAuthenticationManager());
        this.everythingElse = everythingElse;
    }

    protected void doFilterInternal(request, response, chain) {
        if ("/api/v1/notification".equals(request.getPathInfo())) {
            super.doFilterInternal(request, response, chain);
        } else {
            this.everythingElse.doFilterInternal(request, response, chain);
        }
    }
}

然后替换DSL中的过滤器:

http
    .httpBasic()
        .withObjectPostProcessor(
            new ObjectPostProcessor<BasicAuthenticationFilter>() {
                public BasicAuthenticationFilter postProcess(BasicAuthenticationFilter filter) {
                    return new IgnoreFailuresBasicAuthenticationFilter(filter);
                }
            });

这将允许过滤器链继续,即使基本身份验证失败。结果是,如果身份验证失败,您将得到 403 而不是 401。