使用Oauth2或Http-Basic身份验证对同一资源进行春季安全性

我正在尝试使用受Oauth2或Http-Basic身份验证保护的资源实现API。

当我加载WebSecurityConfigurerAdapter时,它首先将http-basic身份验证应用于资源,Oauth2令牌身份验证不被接受。反之亦然。

配置示例:这会将 http-basic 身份验证应用于所有 /user/** 资源

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private LoginApi loginApi;

    @Autowired
    public void setLoginApi(LoginApi loginApi) {
        this.loginApi = loginApi;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(new PortalUserAuthenticationProvider(loginApi));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/users/**").authenticated()
                .and()
            .httpBasic();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

这会将 oauth 令牌保护应用于 /user/** 资源

@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .requestMatchers().antMatchers("/users/**")
        .and()
            .authorizeRequests()
                .antMatchers("/users/**").access("#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.hasScope('read')");
    }
}

我敢肯定,我错过了一些神奇的代码,它告诉春天如果第一个失败,可以尝试两者?

任何帮助将不胜感激。


答案 1

我设法根据Michael Ressler的答案的提示获得了这项工作,但进行了一些调整。

我的目标是允许相同的资源端点上使用 Basic Auth 和 Oauth,例如 /leafcase/123。由于过滤器链的排序,我被困了很长一段时间(可以在FilterChainProxy.filterChains中检查);默认顺序如下:

  • Oauth 身份验证服务器(如果在同一项目中启用)的筛选器链。默认顺序 0(请参见授权服务器安全性配置)
  • Oauth 资源服务器的筛选器链。默认顺序 3(请参见资源服务器配置)。它有一个请求匹配器逻辑,该逻辑匹配除Oauth身份验证端点以外的任何内容(例如,/oauth/token,/oauth/authorize等)。请参阅 ResourceServerConfiguration$NotOauthRequestMatcher.matches())。
  • 对应于 config(HttpSecurity http) 的 filterChains - 默认顺序 100,请参阅 WebSecurityConfigurerAdapter。

由于资源服务器的 filterChains 排名高于 WebSecurityConfigurerAdapter 配置的 filterchain,并且前者几乎匹配每个资源端点,因此 Oauth 资源服务器逻辑始终会针对对资源端点的任何请求启动(即使请求使用 Authorization:Basic 标头)。您会得到的错误是:

{
    "error": "unauthorized",
    "error_description": "Full authentication is required to access this resource"
}

我做了2个更改来获得这项工作:

首先,将 WebSecurityConfigurerAdapter 排序到高于资源服务器的位置(顺序 2 高于顺序 3)。

@Configuration
@Order(2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

其次,让 configure(HttpSecurity) 使用仅与“Authorization: Basic”匹配的客户 RequestMatcher。

@Override
protected void configure(HttpSecurity http) throws Exception {

    http
        .anonymous().disable()
        .requestMatcher(new BasicRequestMatcher())
        .authorizeRequests()
            .anyRequest().authenticated()
            .and()
        .httpBasic()
             .authenticationEntryPoint(oAuth2AuthenticationEntryPoint())
            .and()
        // ... other stuff
 }
 ...
 private static class BasicRequestMatcher implements RequestMatcher {
    @Override
    public boolean matches(HttpServletRequest request) {
        String auth = request.getHeader("Authorization");
        return (auth != null && auth.startsWith("Basic"));
    }
 }

因此,它会在资源服务器的 filterChain 有机会匹配它之前匹配并处理 Basic Auth 资源请求。它还只处理Authorizaiton:Basic资源请求,因此任何带有Authorification:Bearer的请求都会失败,然后由资源服务器的filterChain处理(即,Oauth的过滤器启动)。此外,它的排名低于身份验证服务器(如果在同一项目上启用了身份验证服务器),因此它不会阻止AuthactaitonServer的过滤器链处理对/oauth/token等的请求。


答案 2

这可能与您正在寻找的内容很接近:

@Override
public void configure(HttpSecurity http) throws Exception {
    http.requestMatcher(new OAuthRequestedMatcher())
    .authorizeRequests()
        .anyRequest().authenticated();
}

private static class OAuthRequestedMatcher implements RequestMatcher {
    @Override
    public boolean matches(HttpServletRequest request) {
        String auth = request.getHeader("Authorization");
        // Determine if the client request contained an OAuth Authorization
        return (auth != null) && auth.startsWith("Bearer");
    }
}

这唯一没有提供的是在身份验证不成功时“回退”的方法。

对我来说,这种方法是有道理的。如果用户通过基本身份验证直接为请求提供身份验证,则不需要 OAuth。如果客户端是执行操作的人,那么我们需要此筛选器介入并确保请求已正确进行身份验证。