oauth-2.0 对同一资源使用具有Oauth2或Http-Basic身份验证的Spring安全性

k10s72fa  于 2022-10-31  发布在  Spring
关注(0)|答案(9)|浏览(198)

我正在尝试用受Oauth2或Http-Basic身份验证保护的资源实现API。
当我加载WebSecurityConfigurerAdapter(它首先对资源应用http-basic身份验证)时,Oauth2令牌身份验证不被接受,反之亦然。

**配置示例:**这将对所有/user/**资源应用http基本身份验证

@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();
    }
}

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

@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')");
    }
}

我敢肯定,有一些神奇的代码,我错过了告诉Spring尝试两个如果第一个失败?
如有任何帮助,我们将不胜感激。

r1zk6ea1

r1zk6ea11#

我设法得到这个工作的基础上提示迈克尔雷斯勒的答案,但与一些调整。
我的目标是在相同的资源端点上同时允许Basic Auth和Oauth,例如/leafcase/123。由于filterChains的顺序(可以在FilterChainProxy中检查),我被困了很长一段时间;缺省顺序如下:

  • Oauth验证服务器(如果在同一项目中启用)的filterChains。默认顺序为0(请参阅AuthorizationServerSecurityConfiguration)
  • Oauth资源服务器的filterChains。默认顺序为3(请参阅ResourceServerConfiguration)。它具有一个请求匹配器逻辑,该逻辑匹配除Oauth身份验证端点以外的任何内容(例如,/oauth/token、/oauth/authorize等。请参阅ResourceServerConfiguration$NotOauthRequestMatcher.matches())。
  • 该filterChains对应于配置(HttpSecurity http)--默认顺序为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)使用一个客户RequestMatcher,它只匹配“Authorization:基本”。

@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有机会匹配一个基本身份验证资源请求之前,匹配并处理该请求。它还只处理Authorization:Basic资源请求,因此任何带有Authorization:Bearer的请求都将失败,然后由资源服务器的filterChain处理(即Oauth的过滤器生效)。此外,它的级别低于AuthenticationServer(如果AuthenticationServer在同一个项目上启用),因此它不会阻止AuthenticationServer的filterchain处理对/oauth/token的请求,等等。

ws51t4hk

ws51t4hk2#

这可能接近你要找的内容:

@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");
    }
}

唯一没有提供的是,如果身份验证不成功,可以“后退”。
对我来说,这种方法是有意义的。如果一个用户直接通过Basic auth向请求提供身份验证,那么OAuth就没有必要了。如果客户端是一个代理,那么我们需要这个过滤器介入并确保请求得到正确的身份验证。

0sgqnhkj

0sgqnhkj3#

为什么不反过来做呢?如果没有附加令牌,就绕过资源服务器,然后退回到正常的安全筛选器链。这是资源服务器筛选器停止的方式。

@Configuration
@EnableResourceServer
class ResourceServerConfig : ResourceServerConfigurerAdapter() {

    @Throws(Exception::class)
    override fun configure(resources: ResourceServerSecurityConfigurer) {
        resources.resourceId("aaa")
    }

    /**
     * Resources exposed via oauth. As we are providing also local user interface they are also accessible from within.
     */
    @Throws(Exception::class)
    override fun configure(http: HttpSecurity) {
        http.requestMatcher(BearerAuthorizationHeaderMatcher())
                .authorizeRequests()
                .anyRequest()
                .authenticated()
    }

    private class BearerAuthorizationHeaderMatcher : RequestMatcher {
        override fun matches(request: HttpServletRequest): Boolean {
            val auth = request.getHeader("Authorization")
            return auth != null && auth.startsWith("Bearer")
        }
    }

}
cetgtptt

cetgtptt4#

@kca2ply提供的解决方案非常好用。我注意到浏览器没有发出挑战,所以我对代码做了一些调整,如下所示:

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

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

    // @formatter:off
    http.anonymous().disable()
      .requestMatcher(request -> {
          String auth = request.getHeader(HttpHeaders.AUTHORIZATION);
          return (auth != null && auth.startsWith("Basic"));
      })
      .antMatcher("/**")
      .authorizeRequests().anyRequest().authenticated()
    .and()
      .httpBasic();
    // @formatter:on
  }

  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
    .withUser("user").password("password").roles("USER");
  }
}

同时使用requestMatcher()antMatcher()可以使事情完美地运行。浏览器和HTTP客户端现在将首先质询基本凭据(如果尚未提供)。如果未提供凭据,则福尔斯OAuth2。

bejyjqdl

bejyjqdl5#

您可以将BasicAuthenticationFilter添加到安全筛选器链中,以获取受保护资源上的OAuth2 OR基本身份验证安全。示例配置如下...

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManagerBean;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        final String[] userEndpoints = {
            "/v1/api/airline"
        };

        final String[] adminEndpoints = {
                "/v1/api/jobs**"
            };

        http
            .requestMatchers()
                .antMatchers(userEndpoints)
                .antMatchers(adminEndpoints)
                .antMatchers("/secure/**")
                .and()
            .authorizeRequests()
                .antMatchers("/secure/**").authenticated()
                .antMatchers(userEndpoints).hasRole("USER")
                .antMatchers(adminEndpoints).hasRole("ADMIN");

        // @formatter:on
        http.addFilterBefore(new BasicAuthenticationFilter(authenticationManagerBean),
                UsernamePasswordAuthenticationFilter.class);
    }

}
yvt65v4c

yvt65v4c6#

不能提供给你完整的例子,但这里有一个可以挖掘的提示:
粗略地说,springauth只是从请求(头)中提取auth数据请求过滤器和为该auth提供身份验证对象的身份验证管理器的组合。
因此,要在同一个url上获得基本和oauth,您需要在过滤器链BasicAuthenticationFilter和OAuth2AuthenticationProcessingFilter中安装2个过滤器。
我认为问题在于ConfiguringAdapters适合于更简单的配置,因为它们倾向于相互覆盖。

.httpBasic();

call to ResourceServerConfiguration请注意,您还需要提供2个不同的身份验证管理器:一个用于基本身份验证,一个用于Oauth

ddarikpa

ddarikpa7#

如果有人试图让Spring WebFlux使用这个方法,那么确定是否处理请求的方法称为“securityMatcher”,而不是“requestMatcher”。
也就是说

fun configureBasicAuth(http: ServerHttpSecurity): SecurityWebFilterChain {
    return http
        .securityMatcher(BasicAuthServerWebExchangeMatcher())
        .authorizeExchange()
        ...
siotufzp

siotufzp8#

我认为这是不可能的,有两个身份验证。你可以有基本身份验证和oauth2身份验证,但不同的端点。的方式,因为你做了,第一个配置将克服第二个,在这种情况下,http基本将被使用。

nnvyjq4y

nnvyjq4y9#

在最新版本的Spring Boot中,WebSecurityConfigurerAdapter类别已停用。
要使某些端点具有HTTP基本授权,而使其他端点具有Oauth2授权,解决方案是在HttpSecurityFilterChain Bean调用中的Oauth2配置调用之前设置HTTP基本配置调用。例如:

@Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http

        .authorizeRequests()
        .antMatchers("/auth/basic/endpoints/**","/basic-auth/**").authenticated().and().httpBasic()
        .and()

        .authorizeRequests()
        .antMatchers(HttpMethod.POST,"/auth/oauth2/test/**").hasAnyAuthority("SCOPE_somescope/test")
        .antMatchers(HttpMethod.POST,"/auth/oauth2/other/**").hasAnyAuthority("SCOPE_somescope/other")
        .and()

        .oauth2ResourceServer().jwt().decoder(jwtDecoder())
        .and()

        .accessDeniedHandler(accessDeniedHandler)
        .and()

        .authorizeRequests()
        .anyRequest().permitAll()
        .and()

        .csrf().disable();

        return http.build();
    }

其中,jwtDecoder()必须是在配置类中定义的Bean,如下所示:

@Bean
JwtDecoder jwtDecoder() {
    return JwtDecoders.fromIssuerLocation(issuerUri);
}

此外,您还应该在配置中定义UserDetailsService Bean,例如,对于开发Porpuse而言,最简单的方法是定义InMemoryUserDetailsManager:

@Bean
    public UserDetailsService users() {
        // The builder will ensure the passwords are encoded before saving in memory
        UserDetails admin = User.builder()
                .username("user-basic-auth")
                .password("{bcrypt}"+bcryptPassword)
                .roles("USER", "ADMIN")
                .build();
        return new InMemoryUserDetailsManager(admin);
    }

其中{bcrypt}用于解码存储在某个地方的密码,例如,在数据库表中。如果密码是纯文本的,您也可以在密码前面使用{noop}(不推荐,除非用于测试和开发目的)
所有这些Bean都应在类中定义,注解如下:

@Configuration  
@EnableWebSecurity
public class WebSecurityConfig{
...
}

并且不要忘记在配置文件中指定JWT发布者URI,以便Oauth2正常工作:

spring.security.oauth2.resourceserver.jwt.issuer-uri

相关问题