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
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();
}
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;
}
}
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)
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()
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/**")
...
}
}
}