web-dev-qa-db-fra.com

Pourquoi mon jeton est-il rejeté? Qu'est-ce qu'un identifiant de ressource? "Le jeton invalide ne contient pas d'identifiant de ressource (oauth2-resource)"

J'essaie de configurer OAuth2 pour un projet de printemps. J'utilise une instance partagée UAA ( oauth de Cloud Foundry ) fournie par mon lieu de travail (je n'essaie donc pas de créer un serveur d'autorisation et le serveur d'autorisation est distinct du serveur de ressources). Le client est une application d'une seule page et il obtient un jeton directement du serveur d'autorisation à l'aide de l'autorisation implicite. J'ai la configuration SPA où il ajoute l'en-tête Authorization: Bearer <TOKEN> à chaque appel d'API Web à microservices.

Mon problème concerne maintenant les microservices.

J'essaie d'utiliser ce serveur d'autorisation partagé pour authentifier les microservices. Je comprends peut-être que ces microservices jouent le rôle de serveur de ressources, car ils hébergent les points d'extrémité utilisés par le SPA pour obtenir des données.

J'ai donc essayé de configurer un microservice comme ceci:

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

Maintenant, chaque fois que je tape un /api/** avec le Authorization: Bearer <TOKEN>, je reçois un 403 avec cette erreur:

{
    "error": "access_denied",
    "error_description": "Invalid token does not contain resource id (oauth2-resource)"
}

Donc, voici mes questions:

  • Comment configurer ces microservices pour valider le jeton et insérer une méthode de contrôleur Principal in? J'ai actuellement la configuration où le SPA a envoyé et envoie le jeton et j'ai également la clé publique utilisée pour vérifier la signature du jeton. J'ai également utilisé jwt.io pour tester le jeton et il indique "Signature Verified".
  • Qu'est-ce qu'un identifiant de ressource? Pourquoi en ai-je besoin et pourquoi cela cause-t-il l'erreur ci-dessus? Est-ce une chose unique au printemps?

Merci!

5
Rico Kahler

Spring OAuth attend "aud" claim dans le jeton JWT. La valeur de cette revendication doit correspondre à la valeur resourceId que vous spécifiez votre application Spring (sinon, la valeur par défaut est "oauth2-resource").

Pour résoudre votre problème, vous devez:

1) Connectez-vous à votre UAA partagée et assurez-vous qu’elle inclut la réclamation "aud".

2) Remplacez la valeur de cette affirmation "aud" par "oauth2-resource" ou de préférence dans la mise à jour de votre application Spring resourceId par la valeur de cette revendication, comme suit:

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
         resources.tokenServices(tokenServices());
         resources.resourceId(value from the aud claim you got from UAA server);
    }
11
tsolakp

J'ajoute un problème similaire. Dans mon cas, j’ai utilisé l’authentification jdbc et mon serveur d’autorisation et mon serveur de ressources étaient deux API distinctes.

  • Serveur d'authentification

       @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);
     }
    
  • Serveur de ressources

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

Avec cette configuration, je devenais

    {
       "error": "access_denied",
       "error_description": "Invalid token does not contain resource id 
       (xxxxx)"
     }

Pour résoudre ce problème, je devais ajouter

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