web-dev-qa-db-fra.com

Le serveur de ressources Spring Security OAuth2 renvoie toujours un jeton non valide

J'essaie d'obtenir un serveur OAuth2 en mémoire de base fonctionnant à l'aide des bibliothèques Spring. J'ai suivi le exemple sparklr .

J'ai actuellement configuré le serveur et presque tout fonctionne, mais je ne peux pas accéder à ma ressource restreinte depuis le serveur de ressources.

Mon workflow de test:

  1. Accédez à l'URI autorisé oauth pour démarrer le flux OAuth2: http: // localhost: 8080/server/oauth/authorize? Response_type = code & client_id = client

  2. Rediriger vers la page de connexion: http: // localhost: 8080/server/login

  3. Gérer l'approbation et rediriger vers ma page de redirection configurée avec un paramètre de code: http: // localhost: 8080/client? Code = HMJO4K

  4. Créez une demande GET à l'aide de l'authentification de base en utilisant l'ID et le secret du client ainsi que le type et le code d'autorisation: http: // localhost: 8080/server/oauth/token? Grant_type = autorisation_code & code = HMJO4K

  5. Recevoir un access_token et actualiser un objet jeton en retour

    {access_token: "f853bcc5-7801-42d3-9cb8-303fc67b0453" token_type: "bearer" refresh_token: "57100377-dea9-4df0-adab-62e33f2a1b49" expires_in: 299 scope: "read write"}

  6. Essayez d'accéder à une ressource restreinte en utilisant le access_token: http: // localhost: 8080/server/me? Access_token = f853bcc5-7801-42d3-9cb8-303fc67b045

  7. Recevoir une réponse de jeton non valide

    {error: "invalid_token" error_description: "Jeton d'accès non valide: f853bcc5-7801-42d3-9cb8-303fc67b0453"}

  8. POSTEZ à nouveau l'URI du jeton pour actualiser le jeton: http: // localhost: 8080/server/oauth/token? Grant_type = refresh_token & refresh_token = 57100377-dea9-4df0-adab-62e33f2a1b49

  9. Recevez un nouveau jeton

    {access_token: "ed104994-899c-4cd9-8860-43d5689a9420" token_type: "bearer" refresh_token: "57100377-dea9-4df0-adab-62e33f2a1b49" expires_in: 300 scope: "read write"}}

Je ne suis vraiment pas sûr de ce que je fais mal, mais il semble que tout autre que l'accès à l'URI restreint fonctionne. Voici ma configuration:

@Configuration
public class Oauth2ServerConfiguration {

    private static final String SERVER_RESOURCE_ID = "oauth2-server";

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId(SERVER_RESOURCE_ID);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                .sessionManagement()
                    .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and().requestMatchers()
                    .antMatchers("/me")
                .and().authorizeRequests()
                    .antMatchers("/me").access("#oauth2.clientHasRole('ROLE_CLIENT')")
            ;
        }
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthotizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private ClientDetailsService clientDetailsService;

        @Autowired
        @Qualifier("authenticationManagerBean")
        private AuthenticationManager authenticationManager;

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                .withClient("client")
                    .resourceIds(SERVER_RESOURCE_ID)
                    .secret("secret")
                    .authorizedGrantTypes("authorization_code", "refresh_token")
                    .authorities("ROLE_CLIENT")
                    .scopes("read","write")
                    .redirectUris("http://localhost:8080/client")
                    .accessTokenValiditySeconds(300)
                    .autoApprove(true)
            ;
        }

        @Bean
        public TokenStore tokenStore() {
            return new InMemoryTokenStore();
        }

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints
                .tokenStore(tokenStore())
                .userApprovalHandler(userApprovalHandler())
                .authenticationManager(authenticationManager)
            ;
        }

        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            oauthServer.realm("oauth");
        }

        @Bean
        public ApprovalStore approvalStore() throws Exception {
            TokenApprovalStore store = new TokenApprovalStore();
            store.setTokenStore(tokenStore());
            return store;
        }

        @Bean
        public UserApprovalHandler userApprovalHandler() throws Exception {
            TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
            handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
            handler.setClientDetailsService(clientDetailsService);
            handler.setTokenStore(tokenStore());

            return handler;
        }
    }
}

Y a-t-il quelque chose qui me manque ou que j'aborde mal? Toute aide serait grandement appréciée.

19
jyore

Le problème a fini par être que le serveur de ressources et le serveur d'autorisation n'obtenaient pas la même référence de magasin de jetons. Je ne sais pas comment le câblage ne fonctionnait pas correctement, mais l'utilisation d'un objet fixe dans la classe de configuration a fonctionné comme un charme. En fin de compte, je vais passer à un magasin de jetons soutenu par la persistance, qui n'aurait probablement pas eu de problème.

Merci à @OhadR pour la réponse et l'aide!

En fin de compte, j'ai simplifié la configuration, j'ai suivi le même flux de travail et cela a fonctionné

@Configuration
public class Oauth2ServerConfiguration {

    private static final String SERVER_RESOURCE_ID = "oauth2-server";

    private static InMemoryTokenStore tokenStore = new InMemoryTokenStore();


    @Configuration
    @EnableResourceServer
    protected static class ResourceServer extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.tokenStore(tokenStore).resourceId(SERVER_RESOURCE_ID);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.requestMatchers().antMatchers("/me").and().authorizeRequests().antMatchers("/me").access("#oauth2.hasScope('read')");
        }
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthConfig extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private AuthenticationManager authenticationManager;


        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore).approvalStoreDisabled();
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                .withClient("client")
                    .authorizedGrantTypes("authorization_code","refresh_token")
                    .authorities("ROLE_CLIENT")
                    .scopes("read")
                    .resourceIds(SERVER_RESOURCE_ID)
                    .secret("secret")
            ;
        }
    }
}

Quiconque tombe sur ce post, je recommande de regarder plus les tests unitaires par exemple plutôt que l'exemple complet sparklr/tonr, car il a beaucoup de configuration supplémentaire qui ne sont pas nécessairement nécessaires pour commencer.

5
jyore

Votre étape # 6 est incorrecte - le jeton d'accès ne doit pas être envoyé dans l'URL car il est vulnérable de cette façon. plutôt que GET, utilisez POST.

D'ailleurs, je ne comprends pas votre étape # 1 - pourquoi appelez-vous/oauth/authorize? cela doit être fait implicitement lorsque vous essayez d'obtenir une ressource protégée. Je veux dire, votre flux devrait commencer par:

Essayez d'accéder à une ressource restreinte à l'aide du jeton access_token: http: // localhost: 8080/server/me

Ensuite, la négociation commencera "dans les coulisses": une redirection vers "/ oauth/authorize" etc.

De plus, à l'étape 8, notez que vous ne demandez pas "un autre jeton d'accès", mais plutôt une demande de "rafraîchissement du jeton". Comme si votre jeton d'accès avait expiré.

Remarque : Le fournisseur d'identité et le serveur de ressources doivent partager le tokenStore! Lire ici: serveur de ressources Spring Security OAuth2 pure

HTH

5
OhadR

Cela fonctionne pour moi:

@Configuration
public class Oauth2ServerConfiguration {

    private static final String SERVER_RESOURCE_ID = "oauth2-server";

    @Autowired
    private TokenStore tokenStore;

    @Bean
    public TokenStore tokenStore() {
        return new InMemoryTokenStore();
    }

    @Configuration
    @EnableResourceServer
    protected static class ResourceServer extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.tokenStore(tokenStore).resourceId(SERVER_RESOURCE_ID);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            // ... Not important at this stage
        }
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthConfig extends AuthorizationServerConfigurerAdapter {

        @Autowired
        private AuthenticationManager authenticationManager;


        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore).approvalStoreDisabled();
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            //... Not important at this stage
        }
    }
}
0
fywe