Spring boot 弹簧靴->;未满足的费用例外

Spring boot 弹簧靴->;未满足的费用例外,spring-boot,spring-security-oauth2,Spring Boot,Spring Security Oauth2,我试图用Oauth2+JWT激活安全性,但没有成功。当我尝试在Spring Boot中运行应用程序时,它返回以下错误:通过字段“authenticationManager”表示的未满足的依赖关系,我搜索了internet,没有得到任何帮助 我使用的是SpringBoot:2.1.0.RELEASE 跟随我的课程: 资源服务器: @Configuration @EnableWebSecurity @EnableResourceServer public class ResourceServerCo

我试图用Oauth2+JWT激活安全性,但没有成功。当我尝试在Spring Boot中运行应用程序时,它返回以下错误:通过字段“authenticationManager”表示的未满足的依赖关系,我搜索了internet,没有得到任何帮助

我使用的是SpringBoot:2.1.0.RELEASE

跟随我的课程:

资源服务器:

@Configuration
@EnableWebSecurity
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("us3r").password("pwd").roles("ROLE");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/categorias").permitAll()
                .anyRequest().authenticated()
                .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .csrf().disable();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.stateless(true);
    }

}
@Configuration
@EnableAuthorizationServer
@Component
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient("mobile").secret("p@sswd").scopes("read", "write")
                .authorizedGrantTypes("password", "refresh_token").accessTokenValiditySeconds(20)
                .refreshTokenValiditySeconds(3600 * 24);
    }

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

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        accessTokenConverter.setSigningKey("algaworks");
        return accessTokenConverter;
    }

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

}
授权服务器:

@Configuration
@EnableWebSecurity
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("us3r").password("pwd").roles("ROLE");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/categorias").permitAll()
                .anyRequest().authenticated()
                .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .csrf().disable();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.stateless(true);
    }

}
@Configuration
@EnableAuthorizationServer
@Component
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient("mobile").secret("p@sswd").scopes("read", "write")
                .authorizedGrantTypes("password", "refresh_token").accessTokenValiditySeconds(20)
                .refreshTokenValiditySeconds(3600 * 24);
    }

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

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        accessTokenConverter.setSigningKey("algaworks");
        return accessTokenConverter;
    }

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

}
堆栈跟踪:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'authorizationServerConfig': Unsatisfied dependency expressed through field 'authenticationManager'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.authentication.AuthenticationManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=authenticationManagerBean)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1378) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:575) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863) ~[spring-context-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546) ~[spring-context-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.1.0.RELEASE.jar:2.1.0.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) [spring-boot-2.1.0.RELEASE.jar:2.1.0.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.1.0.RELEASE.jar:2.1.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) [spring-boot-2.1.0.RELEASE.jar:2.1.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) [spring-boot-2.1.0.RELEASE.jar:2.1.0.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) [spring-boot-2.1.0.RELEASE.jar:2.1.0.RELEASE]
    at com.example.algamoney.api.AlgamoneyApiApplication.main(AlgamoneyApiApplication.java:10) [classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.1.0.RELEASE.jar:2.1.0.RELEASE]
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.authentication.AuthenticationManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=authenticationManagerBean)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1646) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1205) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1166) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    ... 24 common frames omitted

请尝试使用
authenticationManager
bean,如下所示:

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

您需要连接一个
AuthenticationManager
的原因是您正在使用
密码
授权流:

.authorizedGrantTypes("password", ...
通过密码授予流,OAuth2客户机将向您提供其客户机凭据以及用户的用户名/密码,并且需要在系统中的某个位置定义该用户。通过
AuthenticationManager
实例公开对这些用户进行身份验证的能力

在授权服务器中,有两组用户:OAuth2API的客户端和应用程序的最终用户(希望登录的用户)

客户机是在一个类中定义的,该类扩展了您拥有的
AuthorizationServerConfigurerAdapter
。您可以在配置
客户端详细信息服务配置器时定义它们

用户是在扩展
websecurityConfigureAdapter
的类中定义的,您没有该类。您可以这样定义它们并公开相应的
AuthenticationManager
bean(注意这是您的主要问题,但是您的配置还有其他问题,请继续阅读):

最后一个方法公开了
AuthenticationManager
bean,以便其他Springbean可以依赖它(比如您的
AuthorizationServerConfig
bean)

为了减少噪音,让我们对其他类进行一些清理,以防遇到额外的障碍

首先,
AuthorizationServerConfig
。使用
@配置
@组件
是多余的。您只需使用
@配置
。此外,这似乎是额外的工作,但在SpringSecurityDSL中使用良好的空白也很有帮助。由于fluentapi,它非常强大,但也很容易让人看不懂

另外,当您进入生产环境时,这会感觉更自然一些,但是您需要指定用于客户端密码的哈希机制。由于您现在没有进行任何哈希运算,这可能会感觉有点奇怪,但对于这个示例来说是可行的

最后,20秒对于访问令牌来说是非常短的。我建议至少几分钟,虽然很多。如果你想让你的用户每天重新登录,那么24小时刷新令牌就可以了。刷新令牌过期后,用户将需要使用其凭据再次登录

根据您现有的配置,我建议您(尽管请继续关注此设置的其他一些问题):

现在,我注意到您的客户的名字是
mobile
对于移动客户端的现代建议是使用,Spring Security目前还不支持这一点。虽然我并不完全了解您的用例(似乎您现在只是想让它正常工作),但我只想指出,移动应用程序不擅长保密,因此您需要选择不需要移动应用程序存储客户端机密的授权类型(而且,
密码
肯定需要客户机密)

好的,第二,
ResourceServerConfig
。我们可以删除
@EnableWebSecurity
,因为我们在新的
SecurityConfig
类中有它。另外,资源服务器通常不维护一组用户(授权服务器有这些用户),所以我们可以删除第一个
configure
方法

根据您现有的配置,我建议:

@Configuration
@EnableResourceServer
public class ResourceServerConfig
    extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/categorias").permitAll()
                .anyRequest().authenticated()
                .and()
           .sessionManagement()
               .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
               .and()
            .csrf().disable();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) 
        throws Exception {
        resources.stateless(true);
    }
}
为了确保这一点,我将所有内容粘贴到IDE中的本地项目中,启动它,并能够执行以下操作:

curl --user "mobile:p@sswd" localhost:8080/oauth/token -dgrant_type=password -dusername=us3r -dpassword=pwd
它带着一个令牌回来了:

{ "access_token":"eyJh...",
  "token_type":"bearer",
  "refresh_token":"eyJh...", 
  "expires_in":199,
  "scope":"read write",
  "jti":"09855c19-7a71-4314-bf8f-eb74689d158c" }
然后我可以做:

export set TOKEN="eyJh..."
如果我这样做:

curl localhost:8080/yo
我得到了401,但如果我包括代币:

curl -H "Authorization: Bearer $TOKEN" localhost:8080/yo
我有一辆404

我意识到这有点反气候,但嘿,这表明我得到了认证。:)


现在我已经说了所有这些,如果您还在阅读,那么我还想指出,根据您的需要,SpringSecurity5.1提供了一个简化的OAuth2.0API。它还没有授权服务器,但它有基于JWT的资源服务器和客户端。我鼓励你看看新的东西是否有你需要的功能。

没有@Qualifier(“authenticationManagerBean”),你会得到什么?@SimonMartinelli感谢你的回复,删除了“@Qualifier(“authenticationManagerBean”)”但继续相同的内容error@TinyOS感谢您的回复,删除了“@限定符(”authenticationManagerBean”)“但是,当我添加传递给我的代码段时,会出现以下错误:authenticationManagerBean()方法对于类型AuthorizationServerConfigurerAdapter未定义