添加 '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。
所以,我的 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。