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')");
}
}
私は行方不明になっている魔法のコードの一部があると確信しています。
どんな援助でも大歓迎です。
私はマイケル・レスラーの答えによるヒントに基づいてこの作品を手に入れることができましたが、いくつかの微調整を行いました。
私の目標は、同じリソースエンドポイント、たとえば/ leafcase/123で基本認証とOauthの両方を許可することでした。filterChainsの順序のためにかなりの時間閉じ込められましたFilterChainProxy.filterChains)で、デフォルトの順序は次のとおりです。
リソースサーバーの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などへのリクエストを処理することを妨げません。
これはあなたが探していたものに近いかもしれません:
@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は不要です。クライアントが演技している場合、このフィルタが介入してリクエストを確認する必要があります適切に認証されます。
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);
}
}
@ 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にフォールスルーします。
両方の認証を取得することは不可能だと思います。基本認証とoauth2認証を使用できますが、エンドポイントは異なります。この方法では、最初の構成が2番目の構成を克服します。この場合、http basicが使用されます。
そして、なぜこれを逆にしないのですか?トークンが添付されていない場合はリソースサーバーをバイパスし、通常のセキュリティフィルターチェーンにフォールバックします。これは、リソースサーバーフィルターが停止する方法です。
@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")
}
}
}
完全な例を提供することはできませんが、Digへのヒントを次に示します。
大まかに言うと、スプリング認証は、リクエスト(ヘッダー)から認証データを抽出するリクエストフィルターと、その認証用の認証オブジェクトを提供する認証マネージャーの組み合わせにすぎません。
したがって、同じURLで基本およびoauthを取得するには、フィルターチェーンBasicAuthenticationFilterおよびOAuth2AuthenticationProcessingFilterに2つのフィルターをインストールする必要があります。
問題は、Adaptersを構成することで、より単純なconfが互いにオーバーライドする傾向があるためです。だから、最初のステップとして移動しよう
.httpBasic();
ResourceServerConfiguration
の呼び出し2つの異なる認証マネージャーも提供する必要があることに注意してください:1つは基本認証用、もう1つはoauth用です