web-dev-qa-db-ja.com

同じリソースのOauth2またはHttp-Basic認証によるSpringセキュリティ

Oauth2 OR Http-Basic認証によって保護されているリソースを使用してAPIを実装しようとしています。

Http-basic認証を最初にリソースに適用するWebSecurityConfigurerAdapterをロードすると、Oauth2トークン認証は受け入れられません。およびその逆。

設定例:これは、すべての/ user/**リソースにhttp-basic認証を適用します

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

私は行方不明になっている魔法のコードの一部があると確信しています。

どんな援助でも大歓迎です。

28
user3613594

私はマイケル・レスラーの答えによるヒントに基づいてこの作品を手に入れることができましたが、いくつかの微調整を行いました。

私の目標は、同じリソースエンドポイント、たとえば/ leafcase/123で基本認証とOauthの両方を許可することでした。filterChainsの順序のためにかなりの時間閉じ込められましたFilterChainProxy.filterChains)で、デフォルトの順序は次のとおりです。

  • Oauth認証サーバー(同じプロジェクトで有効になっている場合)のfilterChains。デフォルトの順序0(AuthorizationServerSecurityConfigurationを参照)
  • OauthリソースサーバーのfilterChains。デフォルトの順序3(ResourceServerConfigurationを参照)。 Oauth認証エンドポイント(/ oauth/token、/ oauth/authorizeなど)以外のものに一致する要求マッチャーロジックがあります。ResourceServerConfiguration$ NotOauthRequestMatcher.matches()を参照してください)。
  • Config(HttpSecurity http)に対応するfilterChains-デフォルトの順序100。WebSecurityConfigurerAdapterを参照してください。

リソースサーバーのfilterChainsは、WebSecurityConfigurerAdapterで構成されたフィルターチェーンによるものよりもランクが高く、前者は実質的にすべてのリソースエンドポイントに一致するため、リソースエンドポイントへのリクエストに対しては、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リソースリクエストのみを処理するため、Authorization:Bearerを持つリクエストはすべて処理され、リソースサーバーのfilterChainによって処理されます(つまり、Oauthのフィルターが作動します)。また、(同じプロジェクトでAuthenticationServerが有効になっている場合)AuthenticationServerよりもランクが低いため、AuthenticaitonServerのフィルターチェーンが/ oauth/tokenなどへのリクエストを処理することを妨げません。

33
kca2ply

これはあなたが探していたものに近いかもしれません:

@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は不要です。クライアントが演技している場合、このフィルタが介入してリクエストを確認する必要があります適切に認証されます。

5
Michael Ressler

BasicAuthenticationFilterをセキュリティフィルターチェーンに追加して、保護されたリソースのOAuth2 OR Basic認証セキュリティを取得できます。設定例は次のとおりです...

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

}
2
httPants

@ 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にフォールスルーします。

1

両方の認証を取得することは不可能だと思います。基本認証とoauth2認証を使用できますが、エンドポイントは異なります。この方法では、最初の構成が2番目の構成を克服します。この場合、http basicが使用されます。

1
raonirenosto

そして、なぜこれを逆にしないのですか?トークンが添付されていない場合はリソースサーバーをバイパスし、通常のセキュリティフィルターチェーンにフォールバックします。これは、リソースサーバーフィルターが停止する方法です。

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

}
0
kboom

完全な例を提供することはできませんが、Digへのヒントを次に示します。

大まかに言うと、スプリング認証は、リクエスト(ヘッダー)から認証データを抽出するリクエストフィルターと、その認証用の認証オブジェクトを提供する認証マネージャーの組み合わせにすぎません。

したがって、同じURLで基本およびoauthを取得するには、フィルターチェーンBasicAuthenticationFilterおよびOAuth2AuthenticationProcessingFilterに2つのフィルターをインストールする必要があります。

問題は、Adaptersを構成することで、より単純なconfが互いにオーバーライドする傾向があるためです。だから、最初のステップとして移動しよう

.httpBasic();

ResourceServerConfigurationの呼び出し2つの異なる認証マネージャーも提供する必要があることに注意してください:1つは基本認証用、もう1つはoauth用です

0
zeldigas