2016-11-19 8 views
9

私は現在、Vaadinスプリングアプリケーションを開発中です。私が言うことができるのは、jdbcTemplate経由でデータベースに問い合わせることで、ユーザーの認証/承認を行う必要があることだけです。この問題を解決するには?私は春のブート1.4.2を使用しています。Springセキュリティ円環ビーン依存

Description: 
The dependencies of some of the beans in the application context form a cycle: 
┌─────┐ 
| jdbcAccountRepository defined in file [repositories\JdbcAccountRepository.class] 
↑  ↓ 
| securityConfiguration.WebSecurityConfig (field services.JdbcUserDetailsServicessecurity.SecurityConfiguration$WebSecurityConfig.userDetailsService) 
↑  ↓ 
| jdbcUserDetailsServices (field repositories.JdbcAccountRepository services.JdbcUserDetailsServices.repository) 
└─────┘ 

元のコードは次のようになります。

AccountRepository:

public interface AccountRepository { 
    void createAccount(Account user) throws UsernameAlreadyInUseException; 
    Account findAccountByUsername(String username); 
} 

JdbcAccountRepository:

@Repository 
public class JdbcAccountRepository implements AccountRepository { 

    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass()); 

    private final JdbcTemplate jdbcTemplate; 
    private final PasswordEncoder passwordEncoder; 

    @Autowired 
    public JdbcAccountRepository(JdbcTemplate jdbcTemplate, PasswordEncoder passwordEncoder) { 
     this.jdbcTemplate = jdbcTemplate; 
     this.passwordEncoder = passwordEncoder; 
    } 

    @Transactional 
    @Override 
    public void createAccount(Account user) throws UsernameAlreadyInUseException { 
     try { 
      jdbcTemplate.update(
       "insert into Account (firstName, lastName, username, password, role) values (?, ?, ?, ?, ?)", 
       user.getFirstName(), 
       user.getLastName(), 
       user.getUsername(), 
       passwordEncoder.encode(
         user.getPassword()), 
         user.getRole() 
      ); 
     } catch (DuplicateKeyException e) { 
      throw new UsernameAlreadyInUseException(user.getUsername()); 
     } 
    } 

    @Override 
    public Account findAccountByUsername(String username) { 
     return jdbcTemplate.queryForObject(
      "select username, password, firstName, lastName, role from Account where username = ?", 
      (rs, rowNum) -> new Account(
        rs.getString("username"), 
        rs.getString("password"), 
        rs.getString("firstName"), 
        rs.getString("lastName"), 
        rs.getString("role")), 
      username 
     ); 
    } 
} 

JdbcUserDetailsS​​ervices:

@Service 
public class JdbcUserDetailsServices implements UserDetailsService { 
    private final Logger LOGGER = LoggerFactory.getLogger(this.getClass()); 

    @Autowired 
    JdbcAccountRepository repository; 

    @Override 
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 
     try { 
      Account account = repository.findAccountByUsername(username); 
      User user = new User(
       account.getUsername(), 
       account.getPassword(), 
       AuthorityUtils.createAuthorityList(
         account.getRole() 
       ) 
      ); 
      return user; 
     } catch (DataAccessException e) { 
      LOGGER.debug("Account not found", e); 
      throw new UsernameNotFoundException("Username not found."); 
     } 
    } 
} 

SecurityConfiguration:

@Configuration 
@ComponentScan 
public class SecurityConfiguration { 

    @Autowired 
    ApplicationContext context; 

    @Autowired 
    VaadinSecurity security; 

    @Bean 
    public PreAuthorizeSpringViewProviderAccessDelegate preAuthorizeSpringViewProviderAccessDelegate() { 
     return new PreAuthorizeSpringViewProviderAccessDelegate(security, context); 
    } 

    @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true) 
    public static class GlobalMethodSecurity extends GlobalMethodSecurityConfiguration { 

     @Bean 
     @Override 
     protected AccessDecisionManager accessDecisionManager() { 
      return super.accessDecisionManager(); 
     } 
    } 

    @Configuration 
    @EnableWebSecurity 
    public static class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

     @Autowired 
     JdbcUserDetailsServices userDetailsService; 

     @Autowired 
     DataSource dataSource; 

     @Bean 
     public PasswordEncoder passwordEncoder() { 
      return NoOpPasswordEncoder.getInstance(); 
     } 

     @Bean 
     public TextEncryptor textEncryptor() { 
      return Encryptors.noOpText(); 
     } 

     /* 
     * (non-Javadoc) 
     * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter 
     * #configure(org.springframework.security.config.annotation.web.builders.WebSecurity) 
     */ 
     @Override 
     public void configure(WebSecurity web) throws Exception { 
      //Ignoring static resources 
      web.ignoring().antMatchers("/VAADIN/**"); 
     } 

     @Override 
     protected void configure(AuthenticationManagerBuilder auth) 
      throws Exception { 
      auth.userDetailsService(userDetailsService); 
     } 

     @Bean(name="authenticationManager") 
     @Override 
     public AuthenticationManager authenticationManagerBean() throws Exception { 
      return super.authenticationManagerBean(); 
     } 

     /* 
     * (non-Javadoc) 
     * @see org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter 
     * #configure(org.springframework.security.config.annotation.web.builders.HttpSecurity) 
     */ 
     @Override 
     protected void configure(HttpSecurity http) throws Exception { 

      http 
       .exceptionHandling() 
        .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")) 
        .and() 
       .authorizeRequests() 
        .antMatchers("/**").permitAll() 
        .and() 
       .csrf().disable(); 
     } 
    } 
} 

PS [1.1.5,1.2.0)、この問題が原因、他の依存関係に(発生しません、私は必要にダウングレード春のブートバージョンが最新使用する場合)

答えて

12

あなたはサイクルを解決するためにsetter-based dependency injectionconstructor-based dependency injectionを置き換えることができ、Spring Framework Reference Documentation次を参照してください。

循環依存

コンストラクタ注入を主に使用する場合は、解決不可能な循環依存シナリオを作成することができます。

例:クラスAはコンストラクタインジェクションによってクラスBのインスタンスを必要とし、クラスBはコンストラクタインジェクションによってクラスAのインスタンスを必要とします。クラスAとBのBeanを相互に注入するように設定すると、Spring IoCコンテナは実行時にこの循環参照を検出し、BeanCurrentlyInCreationExceptionをスローします。

考えられる解決策の1つは、コンストラクタではなくセッタによって設定されるいくつかのクラスのソースコードを編集することです。あるいは、コンストラクタの注入を避け、セッタ注入のみを使用してください。言い換えれば、推奨されていませんが、セッター注入を使用して循環依存を構成できます。

典型的なケース(循環依存性なし)とは異なり、Bean AとBean Bの循環依存性により、完全に初期化される前にBeanの1つが完全に初期化されます(古典的なchicken/eggシナリオ)。

+0

https://ibb.co/d2x4j5を参照してくださいあなたはその@Autowired下に行くヒーローはВсеЕдно@フィールド –

+0

です:おかげで、しかし、その勧告はJavaDocでの一部ではありません。あなたのIDEのプラグインで、それをお勧めしますか?しかし、Spring Securityは、AFAIKにバージョン4.xのBeanを多くコンストラクタインジェクションに変更しました。したがって、一貫した方法はありません。 – dur

4

@Lazyメソッドが好きです。そうすれば私は1つのパターンにこだわることができます。

http://www.baeldung.com/circular-dependencies-in-spring

+0

依存オブジェクトのプロキシ(JavaプロキシまたはCGLIBプロキシ)を作成することができれば、この解決策のみが動作することをあなたの答えで述べるべきです。 – dur