由于jwttokenprovider,我无法注入passwordencoder类

8zzbczxx  于 2021-07-09  发布在  Java
关注(0)|答案(1)|浏览(290)

我正在写一个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
wz8daaqr

wz8daaqr1#

问题是你在注射什么东西 JwtTokenProvider 在一个 @Configuration 类,该类生成 JwtTokenProvider . 所以 JwtTokenProvider 需要安全配置提供的内容,而 SecurityConfig 只能在具有 JwtTokenProvider 因此循环依赖。
删除 @ComponentJwtTokenProvider 创建一个 @Bean 中的方法 SecurityConfig (或不管它叫什么)创造了 JwtTokenProvider .
也不建议使用 @Bean 中的方法 @Components 所以把它移到 SecurityConfig 也。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{

  @Bean
  public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder(8);
  }

  @Bean
  public JwtTokenProvider jwtTokenProvider(UserDetailsService userDetailsService) {
    return new JwtTokenProvider(userDetailsService);
  }

  // other config
}

现在可能还有一个问题 JwtUserDetailsService 需要 UserService 这需要 PasswordEncoder 这又创建了一个循环引用。你最好注射 UserRepository 进入你的 JwtUserDetailsService 并使用它通过登录获取用户。我假设 UserService 只是代表 UserRepository.findByLogin 方法。

@Slf4j
@Service
@RequiredArgsConstructor
public class JwtUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {

        User user = userRepository.findByLogin(login);
        if (user != null) {
          JwtUser jwtUser = JwtUserFactory.create(user);
          log.info("IN loadUserByUsername - user with login: {} successfully loaded", login);
          return jwtUser;
        } else {
          throw new UsernameNotFoundException("Unknown user '"+login+"'");
        }
    }
}

注意:您的 UserDetailsService 未遵守合同(未找到任何用户应抛出 UsernameNotFoundException . 修改后的那个确实履行了那个合同。

相关问题