Reputation: 241
I am getting below error message when I am running my spring boot application.
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| securityConfiguration (field private com.prity.springbootdemo1.service.UserService com.prity.springbootdemo1.config.SecurityConfiguration.userService)
↑ ↓
| userServiceImpl (field private org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder com.prity.springbootdemo1.service.UserServiceImpl.passwordEncoder)
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
Upvotes: 24
Views: 91044
Reputation: 81
Create a new service:
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private IUserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if(user != null) {
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
user.isActive(),
true,
true,
true,
AuthorityUtils.createAuthorityList(user.getRoles().toString())
);
}
else {
throw new UsernameNotFoundException("No user with "
+ "the name " + username + "was found in the database");
}
}
}
Upvotes: 0
Reputation: 41
Problem Explanation (note: you don't really need to read this)
When dealing with circular dependencies in Spring, it's akin to having two friends, A and B, who rely on each other excessively. To resolve this, we can use the @Lazy
annotation, instructing Spring to only create Friend B when Friend A explicitly needs it. Although this might introduce a slight delay the first time Friend B is requested, it helps break the cycle. It's important to be cautious about potential issues, especially in scenarios involving multiple dependencies. While there's a more potent solution with spring.main.allow-circular-references
, it comes with risks and is not always advisable. By employing @Lazy
, we can ensure our friends cooperate without causing any undue trouble.
The better solution depends on the specific context and requirements of your application. Using @Lazy
is a more straightforward and often safer approach, as it defers the creation of the bean until it's actually needed, breaking the circular dependency. On the other hand, enabling spring.main.allow-circular-references
is a more advanced option that might be suitable for specific scenarios but comes with potential runtime issues. It's crucial to assess the trade-offs and choose the solution that aligns with the design and stability goals of your application.
Solution which worked well for me
@Autowired
@Lazy
private BeanB beanB;
(another silly note: using @RequiredArgsConstructor
with @Lazy
, won't work)
Upvotes: 4
Reputation: 31
if a class B is @Autowired
in class A , this means you are directly injecting dependency of class B into that class A. If this causing cyclic dependency, then remove the @Autowired
Annotation and create a constructor and add @Lazy
annotation in the input parameter. The constructor will add the dependency whenever it is required .
class Dog{
@Autowired
private Animal animal;
.....
}
class Animal{
.....
}
If this causing cyclic dependency, then
class Dog{
private Animal animal;
public Dog(@Lazy Animal animal){
return this.animal=animal;
}
Upvotes: 1
Reputation: 355
I've just had the same issue with the code:
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration {
private final AuthenticationProvider authenticationProvider;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
public WebSecurityConfiguration(
AuthenticationProvider authenticationProvider,
JwtAuthenticationFilter jwtAuthenticationFilter
) {
this.authenticationProvider = authenticationProvider;
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests()
.requestMatchers(HttpMethod.POST, "/api/v1/users")
.permitAll()
.anyRequest()
.authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authenticationProvider(authenticationProvider)
.addFilterBefore(
jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration configuration
) throws Exception {
return configuration.getAuthenticationManager();
}
@Bean
public AuthenticationProvider authenticationProvider(
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder
) {
var daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
return daoAuthenticationProvider;
}
}
I then extracted the last three beans to a new class, with @Configuration annotation. It did the trick.
Upvotes: 1
Reputation: 171
So if you have a class A
and you somehow run into this problem when injecting the class A
into the constructor of class B
, use the @Lazy
annotation in the constructor of class B
. This will break the cycle and inject the bean of A
lazily into B
. So, instead of fully initializing the bean, it will create a proxy to inject into the other bean. The injected bean will only be fully created when it's first needed.
@Component
public class CircularDependencyA {
private CircularDependencyB circB;
@Autowired
public CircularDependencyA(@Lazy CircularDependencyB circB) {
this.circB = circB;
}
}
Upvotes: 13
Reputation: 329
Have you upgraded your Spring Boot to 2.6.0 and later? Maybe you should modify your SecurityConfiguration. See this
In my project, I did this. Finially, it works well.
import com.yourweb.filter.JwtAuthenticationTokenFilter;
import com.yourweb.security.AuthenticationEntryPointImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
@Resource
private AuthenticationEntryPointImpl authenticationEntryPoint;
@Resource
private LogoutSuccessHandler logoutSuccessHandler;
@Resource
private JwtAuthenticationTokenFilter authenticationTokenFilter;
@Bean
public AuthenticationManager authManager(
HttpSecurity http,
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder,
UserDetailsPasswordService userDetailsPasswordService) throws Exception {
return http.getSharedObject(AuthenticationManagerBuilder.class)
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder)
.userDetailsPasswordManager(userDetailsPasswordService)
.and()
.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
String idForEncode = "bcrypt";
Map<String, PasswordEncoder> encoders = new HashMap<>(15);
encoders.put(idForEncode, new BCryptPasswordEncoder());
return new DelegatingPasswordEncoder(idForEncode, encoders);
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/auth/login", "/captcha").anonymous()
.antMatchers(
HttpMethod.GET,
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js"
).permitAll()
.antMatchers("/profile/**").anonymous()
.antMatchers("/upload/**").anonymous()
.antMatchers("/common/download**").anonymous()
.antMatchers("/swagger-ui/**").anonymous()
.antMatchers("/swagger-resources/**").anonymous()
.antMatchers("/webjars/**").anonymous()
.antMatchers("/*/api-docs").anonymous()
.antMatchers("/druid/**").anonymous()
.antMatchers("/modeler/**").anonymous()
.antMatchers("/process/general/read-resource/**").anonymous()
.antMatchers("/process/definition/resource/**").anonymous()
.antMatchers("/activiti/getTracePhoto/**").anonymous()
.antMatchers("/process/getTracePhoto/**").anonymous()
.antMatchers("/**/deviceFileMaintenance/addDeviceFileMaintenance").anonymous()
.antMatchers("/**/deviceFileInstall/addDeviceFileInstall").anonymous()
.antMatchers("/**/photoUpload").anonymous()
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable()
.and()
.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)
.and()
.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
}
Upvotes: 2
Reputation: 329
Instead of using Constructor Injection just use the @Autowired
annotation. and also add the following line in resources/application.properties file
spring.main.allow-circular-references=true
For example :
Upvotes: 0
Reputation: 315
You can try with this. Add it to file application.properties
spring.main.allow-circular-references=true
And try to run. This is not best solution you still need to find better way to fix problem.
Upvotes: 18