我正在写一个RESTMVC应用程序。我用的是SpringBoot和hibernate。为了保护,我决定将Spring Security 和jwt添加到项目中。
我用 BCryptEncoder
对密码进行编码。因此,我把它放在 JWTTokenProvider
像豆子一样上课。我需要注射 PasswordEncoder
进入 UserService
同学们,但我不能。我明白原因,但我不知道怎么解决。 UserSevice
:
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository;
private final UserMapper userMapper;
private final PasswordEncoder passwordEncoder;
public UserDTO registration (RegistrationUserDTO registrationUserDTO){
User user = new User();
user.setName(registrationUserDTO.getName());
user.setLastName(registrationUserDTO.getLastName());
user.setLogin(registrationUserDTO.getLogin());
user.setMail(registrationUserDTO.getMail());
user.setPassword(passwordEncoder.encode(registrationUserDTO.getPassword()));
user.setRole(Role.CUSTOMER);
userRepository.save(user);
return userMapper.userToUserDTO(user);
}
}
``` `JwtTokenProvider` :
@RequiredArgsConstructor
@Component
public class JwtTokenProvider {
// Fields
//
private final UserDetailsService userDetailsService;
@Value("${jwt.token.secret}")
private String secret;
@Value("${jwt.token.expired}")
private Long validityInMilliSeconds;
// METHODS
//
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(8);
}
@PostConstruct
protected void init() {
secret = Base64.getEncoder().encodeToString(secret.getBytes());
}
/**
*
* @param login
* @param role
* @return ТОКЕН
*/
public String createToken(String login, Role role) {
Claims claims = Jwts.claims().setSubject(login);
claims.put("roles", role.name());
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliSeconds);
return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(validity)
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public Authentication getAuthentication(String token) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(getLogin(token));
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
public String getLogin(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject();
}
public boolean validateToken(String token) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
return !claims.getBody().getExpiration().before(new Date());
} catch (JwtException | IllegalArgumentException e) {
throw new JwtAuthenticationException("JWT token is expired or invalid");
}
}
/**
*
* @param req
* @return bearerToken
*/
public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
``` JwtUserDetailsService
:
@Slf4j
@Service
@RequiredArgsConstructor
public class JwtUserDetailsService implements UserDetailsService {
private final UserService userService;
@Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
User user = userService.findByLogin(login);
JwtUser jwtUser = JwtUserFactory.create(user);
log.info("IN loadUserByUsername - user with login: {} successfully loaded", login);
return jwtUser;
}
}
日志:
2020-08-20 11:59:44.964 WARN 15396 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'securityConfig' defined in file [D:\JetBrainsProjects\Coffeetearea\build\classes\java\main\ru\coffeetearea\config\SecurityConfig.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jwtTokenProvider' defined in file [D:\JetBrainsProjects\Coffeetearea\build\classes\java\main\ru\coffeetearea\security\jwt\JwtTokenProvider.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'jwtUserDetailsService' defined in file [D:\JetBrainsProjects\Coffeetearea\build\classes\java\main\ru\coffeetearea\security\JwtUserDetailsService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService' defined in file [D:\JetBrainsProjects\Coffeetearea\build\classes\java\main\ru\coffeetearea\service\UserService.class]: Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'jwtTokenProvider': Requested bean is currently in creation: Is there an unresolvable circular reference?
2020-08-20 11:59:44.964 INFO 15396 --- [ main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-08-20 11:59:49.500 INFO 15396 --- [ task-1] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
Exception in thread "task-2" org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'delegatingApplicationListener': Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:212)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207)
at org.springframework.context.event.AbstractApplicationEventMulticaster.retrieveApplicationListeners(AbstractApplicationEventMulticaster.java:245)
at org.springframework.context.event.AbstractApplicationEventMulticaster.getApplicationListeners(AbstractApplicationEventMulticaster.java:197)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:134)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:404)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:361)
at org.springframework.boot.autoconfigure.orm.jpa.DataSourceInitializedPublisher.publishEventIfRequired(DataSourceInitializedPublisher.java:99)
at org.springframework.boot.autoconfigure.orm.jpa.DataSourceInitializedPublisher.access$100(DataSourceInitializedPublisher.java:50)
at org.springframework.boot.autoconfigure.orm.jpa.DataSourceInitializedPublisher$DataSourceSchemaCreatedPublisher.lambda$postProcessEntityManagerFactory$0(DataSourceInitializedPublisher.java:200)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
2020-08-20 11:59:49.525 INFO 15396 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
2020-08-20 11:59:49.532 INFO 15396 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2020-08-20 11:59:49.589 INFO 15396 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
2020-08-20 11:59:49.598 INFO 15396 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2020-08-20 11:59:49.628 INFO 15396 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-08-20 11:59:49.659 ERROR 15396 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
securityConfig defined in file [D:\JetBrainsProjects\Coffeetearea\build\classes\java\main\ru\coffeetearea\config\SecurityConfig.class]
┌─────┐
| jwtTokenProvider defined in file [D:\JetBrainsProjects\Coffeetearea\build\classes\java\main\ru\coffeetearea\security\jwt\JwtTokenProvider.class]
↑ ↓
| jwtUserDetailsService defined in file [D:\JetBrainsProjects\Coffeetearea\build\classes\java\main\ru\coffeetearea\security\JwtUserDetailsService.class]
↑ ↓
| userService defined in file [D:\JetBrainsProjects\Coffeetearea\build\classes\java\main\ru\coffeetearea\service\UserService.class]
└─────┘
Process finished with exit code 1
1条答案
按热度按时间wz8daaqr1#
问题是你在注射什么东西
JwtTokenProvider
在一个@Configuration
类,该类生成JwtTokenProvider
. 所以JwtTokenProvider
需要安全配置提供的内容,而SecurityConfig
只能在具有JwtTokenProvider
因此循环依赖。删除
@Component
从JwtTokenProvider
创建一个@Bean
中的方法SecurityConfig
(或不管它叫什么)创造了JwtTokenProvider
.也不建议使用
@Bean
中的方法@Components
所以把它移到SecurityConfig
也。现在可能还有一个问题
JwtUserDetailsService
需要UserService
这需要PasswordEncoder
这又创建了一个循环引用。你最好注射UserRepository
进入你的JwtUserDetailsService
并使用它通过登录获取用户。我假设UserService
只是代表UserRepository.findByLogin
方法。注意:您的
UserDetailsService
未遵守合同(未找到任何用户应抛出UsernameNotFoundException
. 修改后的那个确实履行了那个合同。