spring circular dependencies - spring

I have the following configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfig.class);
#Resource
private UserDetailsService userDetailsService;
#Resource
private PasswordEncoder passwordEncoder;
.....
#Configuration
#Order(2)
public static class MobileApiSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Resource
private UserDetailsService userDetailsService;
#Resource
private PasswordEncoder passwordEncoder;
#Autowired
private CustomBasicAuthenticationFilter customBasicAuthenticationFilter;
#Autowired
private TokenSecurityFilter tokenSecurityFilter;
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
protected void configure(HttpSecurity http) throws Exception {
http
.addFilter(customBasicAuthenticationFilter)
.addFilterBefore(tokenSecurityFilter, CustomBasicAuthenticationFilter.class)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.csrf().disable()
.authorizeRequests()
.antMatchers(Mappings.MOBILE_API + "/**").hasAnyRole(Globals.MOBILE_API_USER_ROLE)
.and()
.exceptionHandling()
.authenticationEntryPoint(new CustomBasicAuthenticationEntryPoint())
.and()
.requestCache()
.requestCache(new NullRequestCache());
}
}
This is my custom filter:
#Component
public class CustomBasicAuthenticationFilter extends BasicAuthenticationFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomBasicAuthenticationFilter.class);
#Autowired
private PrincipalRepository principalRepository;
#Autowired
private AuthenticationCache authenticationCache;
#Autowired
public CustomBasicAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
#Override
protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException {
Principal principal = principalRepository.findOne(PrincipalPredicates.userNameEquals(authResult.getName()));
if (principal != null) {
principal.setLastLoginTime(DateTime.now());
principalRepository.save(principal);
} else {
LOGGER.error("Unable to retrieve user " + authResult.getName());
}
authenticationCache.add(authResult, request, response);
super.onSuccessfulAuthentication(request, response, authResult);
}
}
But, when trying to deploy to Tomcat, the following exception is thrown:
Error creating bean with name 'customBasicAuthenticationFilter' defined in file [C:\work\...]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.springframework.security.authentication.AuthenticationManager]: : No qualifying bean of type [org.springframework.security.authentication.AuthenticationManager] 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.authentication.AuthenticationManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
I am not sure why my filter is looking for an authenticationManager since I am autowiring it. Help is appreciated it.
Updated Question: I added this code to resolve the authenticationManager issue:
#Bean(name="myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
By adding the above, I am able to get past the authenticationManager issue, but now I am getting this issue:
org.springframework.beans.factory.BeanCreationException:
Could not autowire field: private CustomBasicAuthenticationFilter SecurityConfig$MobileApiSecurityConfigurerAdapter.customBasicAuthenticationFilter;
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'customBasicAuthenticationFilter':
Requested bean is currently in creation: Is there an unresolvable circular reference?
Thanks

You can try to add #EnableWebSecurity annotation on top of MobileApiSecurityConfigurerAdapter class definition.

I added the #Lazy annotation to the filter and I'm able to deploy now.
Feel free to offer other solutions.

Related

Spring Security: Global AuthenticationManager without the WebSecurityConfigurerAdapter

Im trying to get rid of WebSecurityConfigurerAdapter. The AuthenticationManager is configured like the following:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public static class DefaultSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
#Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
Now without the WebSecurityConfigurerAdapter i define the global AuthenticationManager like this:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public static class DefaultSecurityConfig {
#Bean
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth ) throws Exception {
return auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder()).and().build();
}
And i get the error:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'defaultSecurityFilterChain' defined in class path resource [org/springframework/boot/autoconfigure/security/servlet/SpringBootWebSecurityConfiguration.class]: Unsatisfied dependency expressed through method 'defaultSecurityFilterChain' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity' defined in class path resource [org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.config.annotation.web.builders.HttpSecurity]: Factory method 'httpSecurity' threw exception; nested exception is java.lang.IllegalStateException: Cannot apply org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration$EnableGlobalAuthenticationAutowiredConfigurer#536a79bd to already built object
Im using Spring Boot v2.6.4
I'm stuck here for a while i would appreciate any help
Replace
#Bean
public AuthenticationManager authenticationManager(AuthenticationManagerBuilder auth ) throws Exception {
return auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder()).and().build();
}
by
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)
throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
In my project, I needed to use the AuthenticationManager in the Controller, to make a custom login. Then I declare:
#RestController
#RequestMapping("/api/login")
#Slf4j
#RequiredArgsConstructor
public class LoginResource {
private final AuthenticationManager authenticationManager;
....
}
In SecurityConfig:
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig {
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
private final JwtRequestFilter jwtRequestFilter;
private final UserDetailsService jwtUserDetailsService;
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/login").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public AuthenticationManager authenticationManagerBean(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
return authenticationManagerBuilder.build();
}
}
Then Works fine to me!!
Replace your code with this section below.
Hope it works, working fine in my code
#Bean
public AuthenticationManager authenticationManagerBean(HttpSecurity http) throws Exception {
AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
authenticationManagerBuilder
.userDetailsService(jwtUserDetailsService)
.passwordEncoder(passwordEncoder());
return authenticationManagerBuilder.build();
}

How can I provide a Spring configuation bean in a WebSecurityConfigurerAdapter for testing?

I use the following SecurityConfiguration class for securing endpoints in a Spring Boot application. This class depends on the ApiConfiguration class which provides the username and password for the in-memory authentication.
When starting a #WebMvcTest for a controller, then Spring also tries to initialize the security configuration but fails to load the application context.
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'ApiConfiguration' available: expected at least 1 bean which qualifies as autowire candidate.
I tried adding a MockBean to the test class:
#MockBean
private ApiConfiguration apiConfiguration;
This resolves the above issue, but then the username and password are null.
Is there any Spring support for providing this configuration bean for testing?
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
#Configuration
#Order(1)
public static class ApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String API_USER_ROLE = "API_USER";
private final ApiConfiguration apiConfig;
public ApiSecurityConfiguration(ApiConfiguration apiConfig) {
this.apiConfig = apiConfig;
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser(apiConfig.getUsername())
.password(passwordEncoder().encode(apiConfig.getPassword()))
.roles(API_USER_ROLE);
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.formLogin().disable()
.mvcMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole(API_USER_ROLE)
.and()
.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
}
I ended up creating a CustomApiConfiguration annotated with #TestConfiguration. This seems to do the trick. At least, Spring is able to read the properties when setting up the SecurityConfiguration class.
#TestConfiguration
public class CustomApiConfiguration {
#Bean
public ApiConfiguration apiConfiguration() {
final ApiConfiguration config = new ApiConfiguration();
config.setUsername("api-username");
config.setPassword("api-password");
return config;
}
}

Testing spring controller with spring security enabled fails to load application context

I have added spring security to my app and now my controller unit tests have stopped working, failing to load application context. The security I have added generates a jwt token that must be provided to access my API endpoints. The error seems to be it cannot find a qualifying bean for UserDetailsService. Any help would be appreciated. My set up follows:
JwtUserDetailsService:
#Service
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UserRepositoryJWT userRepository;
#Autowired
private PasswordEncoder bcryptEncoder;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntityJWT user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found with username: " + username);
}
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(),
new ArrayList<>());
}
}
Security Config:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecuirtyConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
#Bean
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().and()
.csrf().disable()
.authorizeRequests().antMatchers("/authenticate", "/register").permitAll().
anyRequest().authenticated().and(). exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
My unit test for a controller that the user must be authenticated to hit is:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = ReportingController.class)
public class ReportingController_UT {
#Autowired
private MockMvc mvc;
#MockBean
private ReportingService reportingService;
#Test
public void getDailyTestInfo_WithBuildingIDInURL_ShouldCallgetDailyReportInfoMethod() throws Exception {
when(reportingService.getDailyReportInfo(anyInt())).thenReturn(anyList());
mvc.perform(get(GET_DAILY_REPORTS_URL).characterEncoding(UTF_8)
.contentType(MediaType.APPLICATION_JSON));
verify(reportingService,times(1)).getDailyReportInfo(anyInt());
}
Running the test throws the following exception:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:123)
at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.postProcessFields(MockitoTestExecutionListener.java:95)
at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.injectFields(MockitoTestExecutionListener.java:79)
at org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener.prepareTestInstance(MockitoTestExecutionListener
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.api.jwt.JwtUserDetailsService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1695)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1253)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:636)

Auto-Authentication in Spring Security

I have a Secured Spring MVC project. I would like to auto-authorize users when a new account is successfully created. This is usually done where a new account is created, as follows:
#Controller
#RequestMapping("/spitter")
public class SpitterController {
...
#Inject AuthenticationManager authMgr;
#Inject AccountService accountService;
...
#RequestMapping(value="/register", method=POST)
public String processRegistration(
#ModelAttribute("spitter") #Valid Spitter form,
BindingResult result) {
convertPasswordError(result);
String psswd = form.getPassword();
accountService.registerAccount(toAccount(form), psswd, result);
// Auto-Authentication
Authentication authRequest =
new UsernamePasswordAuthenticationToken(form.getUsername(), psswd);
Authentication authResult = authMgr.authenticate(authRequest);
SecurityContextHolder.getContext()
.setAuthentication(authResult);
return (result.hasErrors() ? VN_REG_FORM : VN_REG_OK);
}
...
}
I'm using Java configuration. My security configuration file is
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
AccountService accountService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.rememberMe()
.tokenRepository(new InMemoryTokenRepositoryImpl())
.tokenValiditySeconds(2419200)
.key("spittrKey")
.and()
.httpBasic()
.realmName("Spittr")
.and()
.authorizeRequests()
.antMatchers("/user").hasAnyAuthority("admin", "user")
.antMatchers("/admin").hasAuthority("admin")
.antMatchers("spitter/me").authenticated()
.antMatchers(HttpMethod.POST, "/spittles").authenticated()
.anyRequest().permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new UserDetailsServiceAdapter(accountService))
.passwordEncoder(passwordEncoder());
}
}
If I were using XML configuration, I would have an authentication manager element:
<authentication-manager alias="authenticationManager">
...
</authentication-manager/>
where the alias is set because one gets an authentication manager from the http element in addition to the authentication-manager element and you have to distinguish between the two.
However, with my configuration, there is apparently no AuthenticationManager being created:
org.springframework.beans.factory.BeanCreationException:
...
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.springframework.security.authentication.AuthenticationManager spittr.web.SpitterController.authMngr; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.authentication.AuthenticationManager] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#javax.inject.Inject()}
...
This is a bit surprising. I thought that at least one such bean would be created by default. I'm not sure what the best solution is.
its not exposed as a spring bean by default, you need to override the authenticationManagerBean() methon on WebSecurityConfigurerAdapter
#Bean(name="myAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Ref: http://docs.spring.io/autorepo/docs/spring-security-javaconfig-build/1.0.0.M1/api-reference/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.html#authenticationManagerBean()

spring-security java config: How to configure Multiple AuthenticationManager instances

I use:
spring boot: 1.1.7
spring-security: 4.0.0.M2
spring-fmk: 4.1.1.RELEASE
Everything is configured with Java Config (including spring-security)
I'm working on a web server project where Authentication: Basic base64Gibberish header are used to authenticate users.
The problem is that depending on the URI the AuthenticationManager is different (because I need 2 different UserDetailsService.
/URI1/** => authManager1
/URI2/** => authManager2
I've tried multiple extensions of WebSecurityConfigurerAdapter with
#Override
#Bean( name = "authManager1" )
public AuthenticationManager authenticationManagerBean() throws Exception
#Override
#Bean( name = "authManager2" )
public AuthenticationManager authenticationManagerBean() throws Exception
to no avail
I always get:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'springSecurityFilterChain'
defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class]: Instantiation of bean failed;
nested exception is org.springframework.beans.factory.BeanDefinitionStoreException:
Factory method [public javax.servlet.Filter org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.springSecurityFilterChain() throws java.lang.Exception]
threw exception; nested exception is java.lang.IllegalArgumentException:
Expecting to only find a single bean for type interface org.springframework.security.authentication.AuthenticationManager,
but found [authManager1, authManager2]
Since I have multiple security filter chains how can I "tell" spring-security to inject different AuthenticationManager in different security filter chains ?
Thanks in advance
P.
You can have multiple http configuration elements, each with its own AuthenticationManager. It could look like that :
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Bean
private AuthenticationManager authenticationManager1() {
// defines first AuthenticationManager
return authenticationManager;
}
#Bean
private AuthenticationManager authenticationManager2() {
// defines second AuthenticationManager
return authenticationManager;
}
#Configuration
#Order(1)
public static class Uri1ApiConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier(authenticationManager1)
private authManager1;
#Override
protected AuthenticationManager authenticationManager() {
return authManager1;
}
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/URI1/**")
...
}
}
#Configuration
#Order(2)
public static class Uri2ApiConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier(authenticationManager2)
private authManager2;
#Override
protected AuthenticationManager authenticationManager() {
return authManager2;
}
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/URI2/**")
...
}
}
}

Resources