在Spring Boot应用启动时,我看到以下消息:
> *************************** APPLICATION FAILED TO START
> ***************************
>
> Description:
>
> Field oauthProps in com.example.authservice.AuthorizationServerConfig
> required a single bean, but 2 were found:
> - OAuthProperties: defined in file [/Users/simeonleyzerzon/abc/spring-security/spring-security-5-oauth-client/auth-service/target/classes/com/example/authservice/config/OAuthProperties.class]
> - kai-com.example.authservice.config.OAuthProperties: defined in null
>
>
> Action:
>
> Consider marking one of the beans as @Primary, updating the consumer
> to accept multiple beans, or using @Qualifier to identify the bean
> that should be consumed
我想知道是什么原因导致了该豆的重复,以及在没有使用@Primary
注释的情况下如何将其删除?不确定上面的 kai-com 软件包(?)来自何处。
这是有问题的豆子:
package com.example.authservice.config;
//@Primary
@Component
@ConfigurationProperties(prefix="kai")
@Setter @Getter
public class OAuthProperties {
private String[] redirectUris;
private String clientId;
private String clientSecret;
private final Token token = new Token();
@Setter @Getter
public static class Token{
private String value;
private String type="";
}
}
和应用程序/配置等:
package com.example.authservice;
import ...
@SpringBootApplication
public class AuthServiceApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServiceApplication.class, args);
}
}
@Controller
class MainController {
@GetMapping("/")
String index() {
return "index";
}
}
@RestController
class ProfileRestController {
@GetMapping("/resources/userinfo")
Map<String, String> profile(Principal principal) {
return Collections.singletonMap("name", principal.getName());
}
}
@Configuration
@EnableResourceServer
class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/resources/**")
.authorizeRequests()
.mvcMatchers("/resources/userinfo").access("#oauth2.hasScope('profile')");
}
}
@Configuration
@EnableAuthorizationServer
@EnableConfigurationProperties(OAuthProperties.class)
class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired private OAuthProperties oauthProps;
private final AuthenticationManager authenticationManager;
AuthorizationServerConfig(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(oauthProps.getClientId())
.secret(oauthProps.getClientSecret())
.authorizedGrantTypes("authorization_code")
.scopes("profile")
.redirectUris(oauthProps.getRedirectUris());
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(this.authenticationManager);
if (oauthProps.getToken().getType().equals("jwt")) {
endpoints.tokenStore(this.tokenStore()).accessTokenConverter(jwtAccessTokenConverter());
}else {
endpoints.tokenEnhancer(eapiTokenEnhancer());
}
}
TokenEnhancer eapiTokenEnhancer() {
return new TokenEnhancer() {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
DefaultOAuth2AccessToken result = new DefaultOAuth2AccessToken(accessToken);
result.setValue(oauthProps.getToken().getValue());
return result;
}
};
}
@Bean
JwtAccessTokenConverter jwtAccessTokenConverter() {
KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource(".keystore-oauth2-demo"), //keystore
"admin1234".toCharArray()); //storepass
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setKeyPair(factory.getKeyPair("oauth2-demo-key")); //alias
return jwtAccessTokenConverter;
}
@Bean
TokenStore tokenStore() {
return new JwtTokenStore(this.jwtAccessTokenConverter());
}
}
@Service
class SimpleUserDetailsService implements UserDetailsService {
private final Map<String, UserDetails> users = new ConcurrentHashMap<>();
SimpleUserDetailsService() {
Arrays.asList("josh", "rob", "joe")
.forEach(username -> this.users.putIfAbsent(
username, new User(username, "pw", true, true, true, true, AuthorityUtils.createAuthorityList("USER","ACTUATOR"))));
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return this.users.get(username);
}
}
@Configuration
@EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
Eclipse似乎也只知道该bean的单个实例:
答案 0 :(得分:4)
将@EnableConfigurationProperties
与@ConfigurationProperties
一起使用时,您将获得一个名为<prefix>-<fqn>
的bean,kai-com.example.authservice.config.OAuthProperties
。 (另请参见the reference guide)。
以这种方式注册
@ConfigurationProperties
bean时,该bean具有常规名称:<prefix>-<fqn>
,其中<prefix>
是@ConfigurationProperties
批注中指定的环境键前缀,并且<fqn>
是bean的全限定名。如果注释不提供任何前缀,则仅使用Bean的完全限定名称。 上例中的bean名称是acme-com.example.AcmeProperties。 (来自参考指南)。
@Component
将导致使用常规类名的小写字符再次注册Bean。属性的另一个实例。
@EnableConfigurationProperties
注释也将自动应用于您的项目,以便从@ConfigurationProperties
配置任何用Environment
注释的现有bean。您可以通过确保MyConfiguration
已经是bean来简化AcmeProperties
的操作,如以下示例所示:(来自参考指南)。
此处的关键是@EnableConfigurationProperties
已被全局应用,并处理任何带有@ConfigurationProperties
注释的bean。
因此,基本上,您现在可以结合使用@ConfigurationProperties
和Spring Boot 2的两种方式来防止这种滥用。这样,您可以编写更好的代码(并稍微减少内存占用和性能)。
因此,删除@Component
或删除@EnableConfigurationProperties
都可以。
答案 1 :(得分:0)
以下更改(删除了@EnableConfigurationProperties
)似乎有助于减轻对@Primary注释的需要:
@Configuration
@EnableAuthorizationServer
//@EnableConfigurationProperties(OAuthProperties.class)
class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired private OAuthProperties oauthProps;
也许有人可以通过该注释来描述辅助bean创建(及其名称空间/程序包分配)的内部Spring机制,该注释似乎导致与@Autowired
的冲突,或者使我指向此行为的适当文档。