依赖注入 Spring Servlet 上下文 (OncePerRequestFilter)

时间:2021-02-10 14:37:33

标签: java spring spring-boot spring-security

我实现了一个 OncePerRequestFilter,在 doFilterInternal() 中,我想使用一个利用率类,该类使用了 JdbcTemplate 和来自属性文件的用户数据。我意识到它无法访问属性文件(数据库连接和变量)中的数据并且始终具有空值。正如我在互联网上发现的那样,因为上下文不同。

我可以在本地成功设置一个新的 jdbc 数据源,但我不想复制代码,所以我想像在 RestControllers (@Value , @Autowired).

任何想法,我如何将这些注入到将在 servlet 过滤器中使用或直接在我的过滤器中使用的利用类中?

谢谢!

更新 - 代码片段:

RestController中,JdbcTemplate的注入工作正常,但在过滤器中我无法注入它,总是抛出nullPointerException

@SpringBootApplication
public class AsdApplication {

    public static void main(String[] args) {
        SpringApplication.run(AsdApplication.class, args);
    }

    public static class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

        @Override
        protected Filter[] getServletFilters() {
            DelegatingFilterProxy delegateFilterProxy = new DelegatingFilterProxy();
            delegateFilterProxy.setTargetBeanName("MyFilter");
            return new Filter[] { delegateFilterProxy };
        }

        @Override
        protected Class<?>[] getRootConfigClasses() {
            return null;
        }

        @Override
        protected Class<?>[] getServletConfigClasses() {
            return null;
        }

        @Override
        protected String[] getServletMappings() {
            return null;
        }
    }
}
@RestController
public class RestCtrl {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @GetMapping("/test")
    public ResponseEntity<String> getTest() {
        String result = jdbcTemplate.queryForObject("<query>", String.class);
        System.out.println("result in ctrl: " + result);
        return new ResponseEntity<>("asd ad asd asd asd", HttpStatus.OK);
    }
}
@Component(value = "MyFilter")
public class MyFilter extends OncePerRequestFilter {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String result = jdbcTemplate.queryForObject("<query>", String.class);
        System.out.println("result in filter: " + result);

        User currentUser = new User("username", "password", new ArrayList<>());
        UsernamePasswordAuthenticationToken authenticatedUser = new UsernamePasswordAuthenticationToken(
                currentUser, null, currentUser.getAuthorities()
        );

        SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
        filterChain.doFilter(request, response);
    }
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
      httpSecurity.csrf().disable().authorizeRequests().anyRequest().authenticated();
      httpSecurity.addFilterBefore(new MyFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}
spring.datasource.url=jdbc:<sqlserver>
spring.datasource.username=<user>
spring.datasource.password=<pass>
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver

3 个答案:

答案 0 :(得分:1)

由于您实际上正在使用 Spring Boot 并希望使其成为 Spring Security 过滤器链的一部分(这是不同的!)您需要做的是

  1. 创建一个 @Bean 方法来创建过滤器并使其成为一个 bean
  2. 创建一个 @Bean 方法并添加一个 FilterRegistration bean 以防止该 bean 被 Spring Boot 注册为过滤器
  3. 配置 Spring Security。
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
      httpSecurity.csrf().disable().authorizeRequests().anyRequest().authenticated();
      httpSecurity.addFilterBefore(myFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

@Bean
public MyFilter myFilter() {
  return new MyFilter();
}

@Bean
public FilterRegistrationBean<MyFilter> myFilterRegistationBean() {
  FilterRegistationBean frb = new FilterRegistrationBean(myFilter());
  frb.setEnabled(false);
  return frb;
}

最后从您的 @Component 中删除 MyFilter,因为您不需要它,它会创建一个额外的实例。之前的所有更改(例如 ApplicationInitializer 等,您可以删除。

注意:当您使用 Spring Security 并以某种方式使用它进行身份验证时,而不是扩展 OncePerRequestFilter 我建议您扩展 Spring Security AbstractAuthenticationProcessingFilter,它可以更好地与 Spring 集成安全性(例如为身份验证、日志记录等触发事件)。

答案 1 :(得分:0)

我看到您正在创建一个 MyFilter 的新实例,而不是使用 Spring 与 @Component(value = "MyFilter") 管理的实例

httpSecurity.addFilterBefore(new MyFilter(), UsernamePasswordAuthenticationFilter.class);

因此您将遇到 NPE,因为 jdbcTemplate 为空。您可以注入由 Spring 管理的实例,而不是创建一个新实例。

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

    @Autowired
    @Qualifier("MyFilter")
    private MyFilter myFilter;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
      httpSecurity.csrf().disable().authorizeRequests().anyRequest().authenticated();
      httpSecurity.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

答案 2 :(得分:-1)

你应该使用这个:

通过这个类,你可以在非 Bean 类中获得不同的 Spring Boot Bean。

@Component
public class ApplicationContextUtils implements ApplicationContextAware {

    private static ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext appContext)
            throws BeansException {
        ctx = appContext;

    }

    public static ApplicationContext getApplicationContext() {
        return ctx;
    }
}

然后在创建它之后,以这种方式获取您的 bean:

ApplicationContext appCtx = ApplicationContextUtils.getApplicationContext();

// Here you get your dependency
ARequiredClass dependency  = appCtx.getBean(ARequiredClass.class);
相关问题