How to autowire this TokenStore

18,952

Solution 1

The error is quite clear, it means, that you don't have a bean of type TokenStore defined. The default configuration of Spring Security OAuth doesn't expose the token store as a bean, so you have to do it by yourself:

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

Use the appropriate implementation here (can also be JwtTokenStore or RedisTokenStore or your own).

Then create a configuration class which extends AuthorizationServerConfigurerAdapter, where you configure that token store for Spring Security OAuth, so that it doesn't create a default one again:

@Configuration
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    TokenStore tokenStore;

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

Then you should be able to inject that TokenStore bean wherever you want.

Solution 2

I was also facing similar issue in my project. I changed the @autowired annotation to constructor dependency. you can try below code.

private TokenStore tokenStore;

@Autowired
public OAuthController(TokenStore tokenStore) {
    this.tokenStore = tokenStore;
}
Share:
18,952
CodeMed
Author by

CodeMed

Updated on June 15, 2022

Comments

  • CodeMed
    CodeMed almost 2 years

    How do I trigger auto-logout of this sample Spring Boot OAuth2 app?

    I tried adding the following code from an answer to this other posting into a new controller class in the demo package of the authserver app:

    package demo;
    
    import javax.servlet.http.HttpServletRequest;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.oauth2.common.OAuth2AccessToken;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseStatus;
    import org.springframework.http.HttpStatus;
    
    @Controller
    public class OAuthController {
        @Autowired
        private TokenStore tokenStore;
    
        @RequestMapping(value = "/oauth/revoke-token", method = RequestMethod.GET)
        @ResponseStatus(HttpStatus.OK)
        public void logout(HttpServletRequest request) {
            String authHeader = request.getHeader("Authorization");
            if (authHeader != null) {
                String tokenValue = authHeader.replace("Bearer", "").trim();
                OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
                tokenStore.removeAccessToken(accessToken);
            }
        }
    }  
    

    But when I try to launch the app, the debug log gives the following error indicating that it cannot autowire the token store:

    Caused by: org.springframework.beans.factory.BeanCreationException:  
    Could not autowire field:  
    private org.springframework.security.oauth2.provider.token.TokenStore  
    demo.OAuthController.tokenStore; nested exception is  
    org.springframework.beans.factory.NoSuchBeanDefinitionException:  
    No qualifying bean of type [org.springframework.security.oauth2.provider.token.TokenStore] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
        ... 23 more
    Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:  
    No qualifying bean of type  
    [org.springframework.security.oauth2.provider.token.TokenStore]  
    found for dependency: expected at least 1 bean which qualifies as  
    autowire candidate for this dependency. Dependency annotations:  
    {@org.springframework.beans.factory.annotation.Autowired(required=true)}
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545        )
        ... 25 more
    

    When the compilation error is resolved, I intend to trigger the logout process from the following code which will be added to the hello.js controller of the ui app in the github link above:

    self.logout = function() {
        $http.post('http://localhost:9000/oauth/revoke-token', {}).finally(function() {
            $rootScope.authenticated = false;
            $location.path("/");
        });
    }
    

    The full code of the sample app is at the github link above, so what specific changes do I need to make in order to successfully autowire the token store and auto-logout the user globally?


    ONGOING EFFORTS:


    Per @ManoJ's suggestion, I changed the new controller class to become the following:

    package demo;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.oauth2.common.OAuth2AccessToken;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseStatus;
    import org.springframework.http.HttpStatus;
    
    @Controller
    public class OAuthController {
    
        private TokenStore tokenStore;
    
        @Autowired
        public OAuthController(TokenStore tokenStore) {
            this.tokenStore = tokenStore;
        }
    
        @RequestMapping(value = "/oauth/revoke-token", method = RequestMethod.GET)
        @ResponseStatus(HttpStatus.OK)
        public void logout(HttpServletRequest request) {
            String authHeader = request.getHeader("Authorization");
            if (authHeader != null) {
                String tokenValue = authHeader.replace("Bearer", "").trim();
                OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
                tokenStore.removeAccessToken(accessToken);
            }
        }
    }
    

    However, I am still getting a very similar error message when I try to launch the app at the terminal with mvn spring-boot:run, as follows:

    java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:497)
        at org.springframework.boot.maven.AbstractRunMojo$LaunchRunner.run(AbstractRunMojo.java:478)
        at java.lang.Thread.run(Thread.java:745)
    Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'OAuthController' defined in file [/home/user/spring_boot_apps/security_tut_remodel/tut-spring-security-and-angular-js/oauth2/authserver/target/classes/demo/OAuthController.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.springframework.security.oauth2.provider.token.TokenStore]: No qualifying bean of type [org.springframework.security.oauth2.provider.token.TokenStore] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.oauth2.provider.token.TokenStore] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
        at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:185)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1143)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1046)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766)
        at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1191)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1180)
        at demo.AuthserverApplication.main(AuthserverApplication.java:88)
        ... 6 more
    Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.oauth2.provider.token.TokenStore] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
        ... 25 more
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD FAILURE
    [INFO] ------------------------------------------------------------------------
    

    Given that this class is the only addition to the sample app in the link in the first line of the OP above, all of the code for the entire app is available both online and on your own devbox with git clone .... So what else needs to change in order for autowire of the TokenStore to work properly?

  • CodeMed
    CodeMed about 8 years
    Thank you and +1. I marked this as accepted because you answered the question. However, to avoid overposting clutter, can you please also show how to use tokenStore in a DIFFERENT controller after you autowire it? For example, the following code from the OP throws a compilation error asking what tokenStore is unless I put it inside your AuthorizationServerConfiguration class: @RequestMapping(value = "/oauth/revoke-token", method = RequestMethod.GET) @ResponseStatus(HttpStatus.OK) public void logout(HttpServletRequest request) { ... tokenStore.readAccessToken(tokenValue);...}
  • CodeMed
    CodeMed about 8 years
    Thank you and +1 for looking into this problem. However, your suggestion left an error until the other user's suggested code was added to successfully inject the @Bean.
  • dunni
    dunni about 8 years
    If you use it how you did it in your OAuthController, it should be fine. Of course you have to define the field tokenStore if you want to use it (and autowire the field or the constructor).
  • Parth Solanki
    Parth Solanki over 7 years
    @dunni Thank you so much. you save my day :)