2017-01-13 4 views
0

ОбновленоКак контролировать Последовательность Bean Создание и компонентов Сканирование Весной ботинке

Я Новичок в весну, и я попытался реализовать приложение безопасности пружинный с использованием конфигурации на основе Java. Но теперь я должен контролировать последовательность создания компонентов и сканирование компонентов приложения.

Это моя конфигурация класса

@EnableWebSecurity 
@Configuration 
class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Autowired 
    private UserDetailsService userDetailsService; 

    @Autowired 
    public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { 
     authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder()); 
    } 

    @Bean 
    public Md5PasswordEncoder passwordEncoder() { 
     return new Md5PasswordEncoder(); 
    } 

    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http.headers().cacheControl(); 
     http.csrf().disable() 
       .authorizeRequests() 
       .antMatchers("/authProxy").permitAll() 
       .antMatchers(HttpMethod.POST,"/login").permitAll() 
       .anyRequest().authenticated() 
       .and() 
       .addFilterBefore(new JWTLoginFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class) 
       .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); 
    } 
} 

и вот JWTLoginFilter

public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter { 

    private TokenAuthenticationService tokenAuthenticationService; 

    public JWTLoginFilter(AuthenticationManager authenticationManager) { 
     super(new AntPathRequestMatcher("/login")); 
     setAuthenticationManager(authenticationManager); 
     tokenAuthenticationService = new TokenAuthenticationService(); 
    } 

    @Override 
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) 
      throws AuthenticationException, IOException, ServletException { 

     AccountCredentials credentials = new ObjectMapper().readValue(httpServletRequest.getInputStream(), AccountCredentials.class); 

     final Authentication authentication = getAuthenticationManager() 
       .authenticate(new UsernamePasswordAuthenticationToken(credentials.getUsername(), 
         credentials.getPassword())); 
     SecurityContextHolder.getContext().setAuthentication(authentication); 
     UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword()); 
     return getAuthenticationManager().authenticate(token); 
    } 

    @Override 
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) 
      throws IOException, ServletException { 
     String name = authentication.getName(); 
     tokenAuthenticationService.addAuthentication(response, name); 
    } 
} 

Это работает отлично. Но все идет не так, когда я пытаюсь объявить JWTLoginFilter как услугу с аннотацией @Service, и пока я пытаюсь это автоустанавливать.

Изменения, которые я выполнял, как следует.

это класс конфигурации.

@EnableWebSecurity 
@Configuration 
class WebSecurityConfig extends WebSecurityConfigurerAdapter { 

    @Autowired 
    private UserDetailsService userDetailsService; 

    @Autowired 
    public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { 
     authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder()); 
    } 

    @Bean 
    public Md5PasswordEncoder passwordEncoder() { 
     return new Md5PasswordEncoder(); 
    } 

    @Bean 
    public AuthenticationManager authenticationManagerBean() throws Exception { 
     return super.authenticationManagerBean(); 
    } 

    @Autowired 
    JWTLoginFilter jwtLoginFilter; 


    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http.headers().cacheControl(); 
     http.csrf().disable() 
       .authorizeRequests() 
       .antMatchers("/authProxy").permitAll() 
       .antMatchers(HttpMethod.POST,"/login").permitAll() 
       .anyRequest().authenticated() 
       .and() 
       .addFilterBefore(jwtLoginFilter, UsernamePasswordAuthenticationFilter.class) 
       .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); 
    } 
} 

И это мой новый JWTLoginFilter

@Service 
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter { 

    @Autowired 
    AuthenticationManager authenticationManager; 

    private TokenAuthenticationService tokenAuthenticationService; 

    public JWTLoginFilter() { 
     super(new AntPathRequestMatcher("/login")); 
     setAuthenticationManager(authenticationManager); 
     tokenAuthenticationService = new TokenAuthenticationService(); 
    } 

    @Override 
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) 
      throws AuthenticationException, IOException, ServletException { 

     AccountCredentials credentials = new ObjectMapper().readValue(httpServletRequest.getInputStream(), AccountCredentials.class); 

     final Authentication authentication = getAuthenticationManager() 
       .authenticate(new UsernamePasswordAuthenticationToken(credentials.getUsername(), 
         credentials.getPassword())); 
     SecurityContextHolder.getContext().setAuthentication(authentication); 
     UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword()); 
     return getAuthenticationManager().authenticate(token); 
    } 

    @Override 
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) 
      throws IOException, ServletException { 
     String name = authentication.getName(); 
     tokenAuthenticationService.addAuthentication(response, name); 
    } 
} 

Этот код дает ошибку во время выполнения под названием

Error starting Tomcat context. Exception: org.springframework.beans.factory.BeanCreationException. Message: Error creating bean with name 'JWTLoginFilter' defined in file [/media/dilanka/Stuff/CODEBASE/Inspection-Application/Inspection-AuthProxy/target/classes/com/shipxpress/inspection/security/jwt/JWTLoginFilter.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: authenticationManager must be specified 

Ошибка в моей мысли в начале, ComponentScan сканирования и инициирование JWTLoginFilter. Но в то время AuthenticationManager bean не создал. Так что это не автоматическая проводка.

Так что я должен создать AuthenticationManager боб перед сканированием JWTLoginFilter, но это не представляется возможным, потому что он должен создать в классе, которая простиралась от WebSecurityConfigurerAdapter и весной позволяет один WebSecurityConfigurerAdapter расширенный класс. Поэтому я не могу инициировать его в другом классе. Также

@Override 
     protected void configure(HttpSecurity http) throws Exception {} 

должен объявить в расширенном классе WebSecurityConfigurerAdapter, и этот метод используют jwtLoginFilter. Так что все

@Autowired 
    JWTLoginFilter jwtLoginFilter; 

и

@Bean 
    public AuthenticationManager authenticationManagerBean() throws Exception { 
     return super.authenticationManagerBean(); 
    } 

и

@Override 
    protected void configure(HttpSecurity http) throws Exception { 
     http.headers().cacheControl(); 
     http.csrf().disable() 
       .authorizeRequests() 
       .antMatchers("/authProxy").permitAll() 
       .antMatchers(HttpMethod.POST,"/login").permitAll() 
       .anyRequest().authenticated() 
       .and() 
       .addFilterBefore(jwtLoginFilter, UsernamePasswordAuthenticationFilter.class) 
       .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); 
    } 

должен определить его в WebSecurityConfig extends WebSecurityConfigurerAdapter class и должен контролировать последовательность боба создания и проверки компонентов приложения. У кого-нибудь есть идея? пожалуйста помогите.

обновляется ->

Я пытался реализовать JWTLoginFilter следующим образом,

@Service 
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter { 

    private TokenAuthenticationService tokenAuthenticationService; 

    @Autowired 
    public JWTLoginFilter(AuthenticationManager authenticationManager) { 
     super(new AntPathRequestMatcher("/login")); 
    } 
... 
} 

Но это дает следующее сообщение об ошибке

The dependencies of some of the beans in the application context form a cycle: 

┌─────┐ 
| JWTLoginFilter defined in file [/media/dilanka/Stuff/CODEBASE/Inspection-Application/DR-136812421-dbchangesSendAsMail/Inspection-Application/Inspection-AuthProxy/target/classes/com/shipxpress/inspection/security/jwt/JWTLoginFilter.class] 
↑  ↓ 
| webSecurityConfig (field com.shipxpress.inspection.security.jwt.JWTLoginFilter com.shipxpress.inspection.config.WebSecurityConfig.jwtLoginFilter) 
└─────┘ 

Я думаю, что проблема в том, Если мы автоматически создадим конструктор, как указано выше, то JWTLoginFilter не может создать без c reating Configuration бобы создания. Но для компонентов конфигурации требуется фасоль JWTLoginFilter. Таким образом, он не может быть создан без компонента JWTLoginFilter.

Спасибо.

ответ

2

@Autowired аннотация будет обработана после был вызван конструктор bean. Таким образом, ваше исключение не зависит от последовательности создания bean-компонентов. Если вам нужно вызвать setAuthenticationManager из конструктора можно применить @Autowired конструктору:

@Service 
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter { 

    AuthenticationManager authenticationManager;  
    private TokenAuthenticationService tokenAuthenticationService; 

    @Autowired 
    public JWTLoginFilter(AuthenticationManager authenticationManager) { 
     this.authenticationManager = authenticationManager; //if you will need this instance in future 
     super(new AntPathRequestMatcher("/login")); 
     setAuthenticationManager(authenticationManager); 
     tokenAuthenticationService = new TokenAuthenticationService(); 
    } 

    ... 
} 

Затем соответствующий боб будет передан в конструктор автоматически.

Другим решением является вся инициализация в методе @PostConstruct. Этот метод будет вызван сразу после обработки @Autowired анкеты:

@Service 
public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter { 

    @Autowired 
    AuthenticationManager authenticationManager;  
    private TokenAuthenticationService tokenAuthenticationService; 

    public JWTLoginFilter(){ 
     super(new AntPathRequestMatcher("/login")); 
    } 

    @PostConstruct 
    public void postConstruct() { 
     setAuthenticationManager(authenticationManager); 
     tokenAuthenticationService = new TokenAuthenticationService(); 
    } 

    ... 
} 
+0

Я пробовал ваше первое решение. Я обновлю проблему с этой реализацией. Он также дает ошибку. Если мы автоматически создадим конструктор, как вы сказали, тогда JWTLoginFilter не может создавать без создания конфигурационного компонента. Но компоненту конфигурации нужен JWTLoginFilter bean. Таким образом, он не может быть создан без компонента JWTLoginFilter. Таким образом, он дает «Зависимости некоторых компонентов в контексте приложения от цикла:». В любом случае, спасибо за ваш ответ. Есть ли у вас предложение избежать этой ошибки? –

0
  • Spring Сапог несколько условных аннотаций использовать как @ConditionalOnBean контролировать последовательность создания боба

  • Посмотрите в пакете org.springframework.boot.autoconfigure.condition для всех доступных условных

  • для вашего примера, лучший способ иметь инъекции конструктора AuthenticationManager в JWTLoginFilter

Смежные вопросы