In a spring boot 2 application with spring security, I try to get local when authentication fail
#Configuration
public class AppConfig {
#Bean
public LocaleResolver localeResolver() {
I18nLocaleResolver r = new I18nLocaleResolver();
r.setDefaultLocale(Locale.ENGLISH);
return r;
}
}
In my CustomAuthenticationFailureHandler who extends SimpleUrlAuthenticationFailureHandler
I have theses attributes
#Autowired
private MessageSource messages;
#Autowired
private LocaleResolver localeResolver;
but when the onAuthenticationFailure method is called, localeResolver and mesages are null
Related
Autowiring works everywhere in application except inside this custom validation annotation class where it is null when called from inside isValid() method.
javax.validation:validation-api: 2.0.1.Final
org.hibernate:hibernate-validator: 5.0.1.Final
spring: 5.1.4.RELEASE
#Component
public class ValidatorUniqueUsername implements ConstraintValidator<UniqueUsername, String> {
#Autowired
AccountService jpaAccountService;
#Override
public void initialize(UniqueUsername constraintAnnotation) { }
#Override
public boolean isValid(String username, ConstraintValidatorContext context) {
return username != null && jpaAccountService.findByUsername(username) == null;
}
}
#Entity
...
public class Account extends BaseEntity<Long> implements Serializable{
#NotEmpty
#UniqueUsername
private String username;
}
#Configuration
public class AppConfig implements AsyncConfigurer {
#Bean
public Validator validatorFactory() {
return new LocalValidatorFactoryBean();
}
#Bean
public static LocalValidatorFactoryBean validatorFactory() {
return new LocalValidatorFactoryBean();
}
Your custom annotation #UniqueUsername instantiates and calls your ValidatorUniqueUsername but it does not inject it even it is annotated with #Component.
And because of this none of the resources to be autowired in your ValidatorUniqueUsername will be injected.
You could try to add this to your #Configuration:
#Bean
public Validator validatorFactory() {
return new LocalValidatorFactoryBean();
}
See more here (excerpt below):
In spring if we register LocalValidatorFactoryBean to bootstrap javax.validation.ValidatorFactory then custom ConstraintValidator classes are loaded as Spring Bean. That means we can have benefit of Spring's dependency injection in validator classes.
I'm seeing the following message on a Spring Boot app startup:
> *************************** 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
I'm wondering what's causing the duplication of that bean and how one can remove it without the necessity of using the #Primary annotation? Not sure where the kai-com package(?) from the above is coming from.
Here's the bean in question:
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="";
}
}
and the app/config, etc.:
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 too seems to be only aware of a single instance of the bean:
When using #EnableConfigurationProperties with #ConfigurationProperties you will get a bean named <prefix>-<fqn>, the kai-com.example.authservice.config.OAuthProperties. (See also the reference guide).
When the #ConfigurationProperties bean is registered that way, the bean has a conventional name: <prefix>-<fqn>, where <prefix> is the environment key prefix specified in the #ConfigurationProperties annotation and <fqn> is the fully qualified name of the bean. If the annotation does not provide any prefix, only the fully qualified name of the bean is used.
The bean name in the example above is acme-com.example.AcmeProperties. (From the Reference Guide).
The #Component will lead to another registration of the bean with the regular name of the classname with a lowercase character. The other instance of your properties.
the #EnableConfigurationProperties annotation is also automatically applied to your project so that any existing bean annotated with #ConfigurationProperties is configured from the Environment. You could shortcut MyConfiguration by making sure AcmeProperties is already a bean, as shown in the following example: (From the Reference Guide).
The key here is that #EnableConfigurationProperties is already globally applied and processes any bean annotated with #ConfigurationProperties.
So basically you where mixing the 2 ways of using #ConfigurationProperties and Spring Boot 2 now prevents that misuse. This way you write better code (and reduce the memory footprint and performance slightly).
So either remove the #Component or remove the #EnableConfigurationProperties, either way will work.
The following change (removing of #EnableConfigurationProperties) seems to help relieving the need for the #Primary annotation:
#Configuration
#EnableAuthorizationServer
//#EnableConfigurationProperties(OAuthProperties.class)
class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired private OAuthProperties oauthProps;
Perhaps someone can describe the internal Spring mechanics of secondary bean creation (and its namespace/package assignment) by that annotation which seemingly causes the collision with the #Autowired one, or point me to the appropriate documentation of this behavior.
I use #Primary and #Profile to mock a bean in Spring test:
#Profile("test")
#Configuration
public class TestBeanConf {
#Bean
#Primary
public UserService userService() {
UserService userService = Mockito.mock(UserService.class);
TokenValidationUrl validation = new TokenValidationUrl();
validation.setValid(true);
validation.setUid("123456789");
Mockito.when(userService.tokenValidation("23456")).thenReturn(validation);
return userService;
}
But other methods of UserService bean return null, how can i spy the real created bean and only mock tokenValidation method?
To spy one method of UserService bean, during construction of this bean we autowire existing instance of UserService from Spring context by sending it as paramter and use Mockito’s spying feature:
#Profile("test")
#Configuration
public class TestBeanConf {
#Bean
#Primary
public UserService userServiceTest(UserService userService) {
UserService userService = Mockito.spy(userService);
TokenValidationUrl validation = new TokenValidationUrl();
validation.setValid(true);
validation.setUid("123456789");
Mockito.when(userService.tokenValidation("23456")).thenReturn(validation);
return userService;
}
i have the following JAVA supported in Spring 4.0.3 configuration, that have two DataSource und JdbcTemplate beans:
#PropertySource("classpath:db.properties")
#Configuration
public class DBConfiguration {
.....
#Autowired
Environment env;
#Bean
public DataSource internalDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// init dataSource setters for DATABASE_1
return dataSource;
}
#Bean
public DataSource publicDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// init dataSource setters for DATABASE_2
return dataSource;
}
}
...
#Bean
public JdbcTemplate internalJDBCTemplate() {
return new JdbcTemplate(internalDataSource());
}
#Bean
public JdbcTemplate publicJDBCTemplate() {
return new JdbcTemplate(publicDataSource());
}
___
I have other configuration bean class, that autowires the first configuration and calls internalDataSource() method:
#Import(DBConfiguration.class)
#Configuration
public class AuthConfiguration {
#Autowired
private DBConfiguration dbConfiguration;
#Autowired
private TokenStore tokenStore;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dbConfiguration.securityDataSource());
}
...}
I suppose, that even DBConfiguration was imported and autowired into AuthConfiguration class, the each call of dbConfiguration.securityDataSource() will be cause the new DriverManagerDataSource() with each time intializing of data source.
Is it correkt or not?
The default bean scope in Spring is singleton so the data source will be initialized only once.
When a DI container creates bean TokenStore it gets a bean defined in DBConfiguration by the securityDataSource method. But it doesn't call the method directly, it takes a bean instance from the DI container. An initialization of all beans is done by Spring transparently to a developer.
Note that classes annotated with #Configuration are just a definition for the framework and they aren't executed directly.
I need to inject an object into my No XML Spring #Configuration object as follows:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "web.client")
public class WebApplicationConfiguration extends WebMvcConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(WebApplicationConfiguration.class);
#Inject
private MonitoringExceptionResolver resolver; // always null
#Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
log.debug("configuring exception resolvers");
super.configureHandlerExceptionResolvers(exceptionResolvers);
exceptionResolvers.add(new DefaultHandlerExceptionResolver());
exceptionResolvers.add(new AnnotationMethodHandlerExceptionResolver());
exceptionResolvers.add(new ResponseStatusExceptionResolver());
exceptionResolvers.add(resolver); // passing null ref here
}
}
Where MonitoringExceptionResolver is defined as follows:
#Service
public class MonitoringExceptionResolver implements HandlerExceptionResolver {
private final Counters counters;
#Inject
public MonitoringExceptionResolver(Counters counters) {
super();
this.counters = counters;
}
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
Counter counter = counters.getCounterFor(ex.getClass());
if(counter != null) {
counter.increment();
}
return null;
}
}
However, I get NPE later in the execution chain because the "resolver" field above is null, even if I use #Autowired.
Other classes are being successfully wired in elsewhere using component scanning. Why is it always null in the above? Am I doing something wrong?
#Inject and #Autowired should work very similar in Spring.
Make sure that *BeanPostProcessor in use is aware of MonitoringExceptionResolver: mark it as #Component and make is subject of some #ComponentScan or make a #Bean factory method is some #Configuration class in use.