春のプロジェクト用にOAuth2を設定しようとしています。共有UAA( クラウドファウンドリからのoauth実装 )インスタンスを使用しています(したがって、承認サーバーを作成しようとしておらず、承認サーバーはリソースサーバーとは別です)。フロントエンドは単一ページのアプリケーションであり、暗黙的な許可を使用して認可サーバーから直接トークンを取得します。マイクロサービスへの各Web API呼び出しにAuthorization: Bearer <TOKEN>
ヘッダーを追加するSPAセットアップがあります。
私の問題はマイクロサービスにあります。
この共有認証サーバーを使用して、マイクロサービスを認証しようとしています。ここで誤解があるかもしれませんが、これらのマイクロサービスは、SPAがデータを取得するために使用するエンドポイントをホストするため、リソースサーバーの役割を果たしているという私の現在の理解を購入します。
だから私はそのようなマイクロサービスを設定しようとしました:
@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setVerifierKey("-----BEGIN PUBLIC KEY-----<key omitted>-----END PUBLIC KEY-----");
return converter;
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(tokenServices());
}
}
/api/**
でAuthorization: Bearer <TOKEN>
をヒットするたびに、このエラーで403
を取得します:
{
"error": "access_denied",
"error_description": "Invalid token does not contain resource id (oauth2-resource)"
}
Principal
を挿入するにはどうすればよいですか? SPAがトークンを所有して送信する場所をセットアップし、トークンの署名を検証するために使用する公開キーも持っています。また、トークンのテストに jwt.io を使用しましたが、「Signature Verified」と表示されます。ありがとう!
Spring OAuthは、「aud」を期待します claim JWTトークン。そのクレームの値は、Springアプリを指定するresourceId
の値と一致する必要があります(指定しない場合)デフォルトは「oauth2-resource」です)。
問題を修正するには、次を行う必要があります。
1)共有UAAにログインし、「aud」クレームが含まれていることを確認します。
2)その「aud」クレームの値を「oauth2-resource」に変更するか、Springアプリの更新resourceId
で次のようにそのクレームの値に変更します。
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(tokenServices());
resources.resourceId(value from the aud claim you got from UAA server);
}
同様の問題を追加します。私の場合、jdbc認証を使用し、承認サーバーとリソースサーバーは2つの別個のAPIでした。
認証サーバー
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.passwordEncoder(oauthClientPasswordEncoder);
}
/**
* Define the client details service. The client may be define either as in memory or in database.
* Here client with be fetch from the specify database
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
/**
* Define the authorization by providing authentificationManager
* And the token enhancement
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore())
.tokenEnhancer(getTokenEnhancer())
.authenticationManager(authenticationManager).userDetailsService(userDetailsService);
}
リソースサーバー
public class OAuth2ResourceServerConfig extends
ResourceServerConfigurerAdapter {
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
@Autowired
private DataSource dataSource;
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// We don't want to allow access to a resource with no token so clear
// the security context in case it is actually an OAuth2Authentication
if (tokenExtractor.extract(request) == null) {
SecurityContextHolder.clearContext();
}
filterChain.doFilter(request, response);
}
}, AbstractPreAuthenticatedProcessingFilter.class);
http.csrf().disable();
http.authorizeRequests().anyRequest().authenticated();
}
@Bean
public AccessTokenConverter accessTokenConverter() {
return new DefaultAccessTokenConverter();
}
@Bean
public RemoteTokenServices remoteTokenServices(final @Value("${auth.server.url}") String checkTokenUrl,
final @Value("${auth.resource.server.clientId}") String clientId,
final @Value("${auth.resource.server.clientsecret}") String clientSecret) {
final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl(checkTokenUrl);
remoteTokenServices.setClientId(clientId);
remoteTokenServices.setClientSecret(clientSecret);
remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
return remoteTokenServices;
}
この構成では、私は
{
"error": "access_denied",
"error_description": "Invalid token does not contain resource id
(xxxxx)"
}
これを解決するには、追加する必要がありました
private String resourceIds= "xxxxx". !! maked sure that this resourceids is store in oauth_client_details for the clientid I used to get the token
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceIds).tokenStore(tokenStore());
}