当前位置: 首页 > 知识库问答 >
问题:

应用程序上下文中的依赖项形成一个循环

常永怡
2023-03-14

应用程序上下文中一些bean的依赖关系形成了一个循环:

   authController defined in file [...\AuthController.class]
      ↓
   userServiceImpl defined in file [...\UserServiceImpl.class]
      ↓
   jwtManager defined in file [...\JwtManager.class]
┌─────┐
|  securityConfig defined in file [...\SecurityConfig.class]
└─────┘

我试过用@Lazy,但没用。

当我尝试将构造函数更改为setter时,它会给我错误,例如:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userServiceImpl' defined in file [...\UserServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 3;

证券配置

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final Logger LOG = LoggerFactory.getLogger(getClass());
    private UserDetailsService userService;
    private PasswordEncoder bCryptPasswordEncoder;
    private ObjectMapper mapper;

    @Value("${app.security.jwt.keystore-location}")
    private String keyStorePath;
    @Value("${app.security.jwt.keystore-password}")
    private String keyStorePassword;
    @Value("${app.security.jwt.key-alias}")
    private String keyAlias;
    @Value("${app.security.jwt.private-key-passphrase}")
    private String privateKeyPassphrase;

    public SecurityConfig(UserDetailsService userService,
                          PasswordEncoder bCryptPasswordEncoder, ObjectMapper mapper) {
        this.userService = userService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
        this.mapper = mapper;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic().disable().formLogin().disable()
              .csrf().ignoringAntMatchers(API_URL_PREFIX, H2_URL_PREFIX)
              .and()
              .headers().frameOptions().sameOrigin() // for H2 Console
              .and()
              .cors()
              .and()
              .authorizeRequests()
              .antMatchers(HttpMethod.POST, TOKEN_URL).permitAll()
              .antMatchers(HttpMethod.DELETE, TOKEN_URL).permitAll()
              .antMatchers(HttpMethod.POST, SIGNUP_URL).permitAll()
              .antMatchers(HttpMethod.POST, REFRESH_URL).permitAll()
              .antMatchers(HttpMethod.GET, PRODUCTS_URL).permitAll()
              .mvcMatchers(HttpMethod.POST, "/api/v1/restaurants**")
              .hasAuthority(RoleEnum.ADMIN.getAuthority())
              .anyRequest().authenticated()
              .and()
              /* Filter based security configuration
              .exceptionHandling().accessDeniedHandler(accessDeniedHandler)
              .and()
              .httpBasic()
              .authenticationEntryPoint(authenticationEntryPoint)
              .and()
              .addFilterBefore(failureHandler , BearerTokenAuthenticationFilter.class)
              .addFilter(new LoginFilter(super.authenticationManager(), mapper))
              .addFilter(new JwtAuthenticationFilter(super.authenticationManager()))
              */
              .oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer.jwt(
                    jwt -> jwt.jwtAuthenticationConverter(getJwtAuthenticationConverter())))
              .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    private Converter<Jwt, AbstractAuthenticationToken> getJwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter authorityConverter = new JwtGrantedAuthoritiesConverter();
        authorityConverter.setAuthorityPrefix(AUTHORITY_PREFIX);
        authorityConverter.setAuthoritiesClaimName(ROLE_CLAIM);
        JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(authorityConverter);
        return converter;
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
    }

    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        return userService;
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH"));
        //configuration.setAllowCredentials(true);
        // For CORS response headers
        configuration.addAllowedOrigin("*");
        configuration.addAllowedHeader("*");
        configuration.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Bean
    public KeyStore keyStore() {
        try {
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            InputStream resourceAsStream = Thread.currentThread().getContextClassLoader()
                  .getResourceAsStream(keyStorePath);
            keyStore.load(resourceAsStream, keyStorePassword.toCharArray());
            return keyStore;
        } catch (IOException | CertificateException | NoSuchAlgorithmException | KeyStoreException e) {
            LOG.error("Unable to load keystore: {}", keyStorePath, e);
        }
        throw new IllegalArgumentException("Unable to load keystore");
    }

    @Bean
    public RSAPrivateKey jwtSigningKey(KeyStore keyStore) {
        try {
            Key key = keyStore.getKey(keyAlias, privateKeyPassphrase.toCharArray());
            if (key instanceof RSAPrivateKey) {
                return (RSAPrivateKey) key;
            }
        } catch (UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException e) {
            LOG.error("Unable to load private key from keystore: {}", keyStorePath, e);
        }
        throw new IllegalArgumentException("Unable to load private key");
    }

    @Bean
    public RSAPublicKey jwtValidationKey(KeyStore keyStore) {
        try {
            Certificate certificate = keyStore.getCertificate(keyAlias);
            PublicKey publicKey = certificate.getPublicKey();
            if (publicKey instanceof RSAPublicKey) {
                return (RSAPublicKey) publicKey;
            }
        } catch (KeyStoreException e) {
            LOG.error("Unable to load private key from keystore: {}", keyStorePath, e);
        }
        throw new IllegalArgumentException("Unable to load RSA public key");
    }

    @Bean
    public JwtDecoder jwtDecoder(RSAPublicKey rsaPublicKey) {
        return NimbusJwtDecoder.withPublicKey(rsaPublicKey).build();
    }

UserServiceImpl

@Service
public class UserServiceImpl implements UserService {

    private final UserRepository repository;
    private final UserTokenRepository userTokenRepository;
    private final PasswordEncoder bCryptPasswordEncoder;
    private final JwtManager tokenManager;


    public UserServiceImpl(UserRepository repository, UserTokenRepository userTokenRepository,
                           PasswordEncoder bCryptPasswordEncoder, JwtManager tokenManager) {
        this.repository = repository;
        this.userTokenRepository = userTokenRepository;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
        this.tokenManager = tokenManager;
    }

JWTManager JWTManager是一个自定义类,负责生成新的JWT。它使用来自auth0的java jwt库。通用域名格式。我使用公钥/私钥对令牌进行签名。

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;

import static java.util.stream.Collectors.toList;
import static the.postbooking.app.security.Constants.EXPIRATION_TIME;
import static the.postbooking.app.security.Constants.ROLE_CLAIM;

@Component
public class JwtManager {

    private final RSAPrivateKey privateKey;
    private final RSAPublicKey publicKey;

    public JwtManager(RSAPrivateKey privateKey, RSAPublicKey publicKey) {
        this.privateKey = privateKey;
        this.publicKey = publicKey;
    }

    public String create(UserDetails principal) {
        final long now = System.currentTimeMillis();
        return JWT.create()
              .withIssuer("The Post Booking-App")
              .withSubject(principal.getUsername())
              .withClaim(ROLE_CLAIM,
                    principal.getAuthorities().stream().map(GrantedAuthority::getAuthority)
                          .collect(toList()))
              .withIssuedAt(new Date(now))
              .withExpiresAt(new Date(now + EXPIRATION_TIME))
              //.sign(Algorithm.HMAC512(SECRET_KEY.getBytes(StandardCharsets.UTF_8)));
              .sign(Algorithm.RSA256(publicKey, privateKey));
    }
}

AuthController用@RestController注释AuthController类,将其标记为REST控制器。然后,它使用了两个bean,UserService和PasswordEncoder,它们将在AuthController构造时被注入。

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.RestController;
import postbookingapp.api.*;
import the.postbooking.app.entity.UserEntity;
import the.postbooking.app.exception.InvalidRefreshTokenException;
import the.postbooking.app.service.UserService;

import javax.validation.Valid;

import static org.springframework.http.ResponseEntity.*;

@RestController
public class AuthController implements UserApi {

    private final UserService service;
    private final PasswordEncoder passwordEncoder;

    public AuthController(UserService service, PasswordEncoder passwordEncoder) {
        this.service = service;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public ResponseEntity<SignedInUser> signIn(@Valid SignInReq signInReq) {
        UserEntity userEntity = service.findUserByUsername(signInReq.getUsername());
        if (passwordEncoder.matches(
              signInReq.getPassword(),
              userEntity.getPassword())) {
            return ok(service.getSignedInUser(userEntity));
        }
        throw new InsufficientAuthenticationException("Unauthorized.");
    }

    @Override
    public ResponseEntity<Void> signOut(@Valid RefreshToken refreshToken) {
        service.removeRefreshToken(refreshToken);
        return accepted().build();
    }

    @Override
    public ResponseEntity<SignedInUser> signUp(@Valid User user) {
        return status(HttpStatus.CREATED).body(service.createUser(user).get());
    }

    @Override
    public ResponseEntity<SignedInUser> getAccessToken(@Valid RefreshToken refreshToken) {
        return ok(service.getAccessToken(refreshToken)
              .orElseThrow(InvalidRefreshTokenException::new));
    }
}

共有1个答案

景国兴
2023-03-14

尝试添加spring。主要的在应用程序中允许循环引用=true。属性文件

 类似资料:
  • 我正在使用JPA开发Spring Boot v1.4.2.RELEASE应用程序。 我定义了存储库接口和实现 存储库 A存储自定义 ARepositoryImpl公司 和一个服务AServiceImpl 我的应用程序不会以以下消息开始: 我遵循http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositorie

  • 我试图创建一个jwt与Spring安全,但当我启动程序,我采取这个错误从Spring: İt也是一个spring web程序,因此该程序与控制器一起工作。此控制器用于创建带有登录名的jwt。以下是我的authController类: 这里是WebServiceConfig类: 以下是UserDetailsService: 谢谢你的帮助阿莱尔迪

  • 几周前,我将Spring的版本从1.4.x.RELEASE升级到1.5.1.RELEASE。 突然之间,由于这个错误,启动Spring Boot服务成了一场斗争: “应用程序上下文中某些bean的依赖关系形成一个循环” 同样的代码,不同的版本 这很有趣,因为在我的本地环境(OSX)上,相同的代码通常都可以正常启动,但在Heroku上,在不同的部署上会随机失败(在类路径类解析上看起来是不同的顺序,由

  • 当我运行我的应用程序时,我得到了下面显示的错误,即应用程序上下文中一些bean的依赖关系形成了一个循环。我不确定问题到底是从哪里来的。对于Spring Boot/Spring Security性来说还是相对较新的,所以如果您能在我的代码上提供任何提示,我们将不胜感激。 UserServiceImpl 安全配置

  • 启动Spring Boot应用程序时,我遇到了一个错误。 现在,我想在DAO存储库中使用,因为Spring Boot不会自动创建。所以,我从EntityManagerFactory创建了bean并尝试在DAO类中自动装配它。 但我有以下错误: 我不明白为什么SessionFactory指的是FooDao类。 以下是代码示例: 食物ao.java Config.java 富。Java语言 测试应用程

  • 问题内容: 我有2个项目,其中一个包含在另一个构建路径中,两个项目都有自己的应用程序上下文来定义他的bean。 我想管理全局事务,因此我必须在一个应用程序上下文中同时导入这两个事务,但是我没有找到做到这一点的方法。 在不同的项目中可能有2个应用程序上下文,并将其中一个导入另一个。 如果没有可能,那将是最佳解决方案? 我认为成为1中的2个项目。 问题答案: 是的,可以导入;在您的一个appconte