无法在Spring中的身份验证筛选器内自动装配服务

Rag*_*dra 45 java spring hibernate spring-mvc

我试图通过令牌验证用户,但当我尝试自动连接我的服务内部AuthenticationTokenProcessingFilter我得到空指针异常.因为自动服务的服务为空,我该如何解决这个问题呢?

我的AuthenticationTokenProcessingFilter班级

@ComponentScan(basePackages = {"com.marketplace"})
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {

    @Autowired
    @Qualifier("myServices")
    private MyServices service;

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        @SuppressWarnings("unchecked")
        Map<String, String[]> parms = request.getParameterMap();

        if (parms.containsKey("token")) {
            try {
                String strToken = parms.get("token")[0]; // grab the first "token" parameter

                User user = service.getUserByToken(strToken);
                System.out.println("Token: " + strToken);

                DateTime dt = new DateTime();
                DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
                DateTime createdDate = fmt.parseDateTime(strToken);
                Minutes mins = Minutes.minutesBetween(createdDate, dt);


                if (user != null && mins.getMinutes() <= 30) {
                    System.out.println("valid token found");

                    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
                    authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));

                    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getEmailId(), user.getPassword());
                    token.setDetails(new WebAuthenticationDetails((HttpServletRequest) request));
                    Authentication authentication = new UsernamePasswordAuthenticationToken(user.getEmailId(), user.getPassword(), authorities); //this.authenticationProvider.authenticate(token);

                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }else{
                    System.out.println("invalid token");
                }
            } catch(Exception e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("no token found");
        }
        // continue thru the filter chain
        chain.doFilter(request, response);
    }
}
Run Code Online (Sandbox Code Playgroud)

我试着加入我的 AppConfig

@Bean(name="myServices")
    public MyServices stockService() {
        return new MyServiceImpl();
    }
Run Code Online (Sandbox Code Playgroud)

我的AppConfig注释是

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.marketplace")
public class AppConfig extends WebMvcConfigurerAdapter {
Run Code Online (Sandbox Code Playgroud)

Hai*_*man 42

您不能使用开箱即用的过滤器依赖注入.虽然您使用的是GenericFilterBean,但您的Servlet过滤器不受spring管理.正如javadocs所指出的那样

此通用过滤器基类不依赖于Spring org.springframework.context.ApplicationContext概念.过滤器通常不加载它们自己的上下文,而是从Spring根应用程序上下文访问服务bean,可以通过过滤器的ServletContext访问(请参阅org.springframework.web.context.support.WebApplicationContextUtils).

用简单的英语我们不能指望spring注入服务,但我们可以在第一次调用时设置它.例如

public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
    private MyServices service;
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if(service==null){
            ServletContext servletContext = request.getServletContext();
            WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
            service = webApplicationContext.getBean(MyServices.class);
        }
        your code ...    
    }

}
Run Code Online (Sandbox Code Playgroud)


Rag*_*dra 20

我只是添加了它

SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(本);

我不确定为什么我们应该这样做,即使我尝试添加显式限定符.现在代码看起来像

public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);

        @SuppressWarnings("unchecked")
        Map<String, String[]> parms = request.getParameterMap();

        if (parms.containsKey("token")) {
Run Code Online (Sandbox Code Playgroud)

  • 您可能需要考虑将其添加到@PostConstruct方法,以防止在每次doFilter()调用中执行它. (3认同)
  • 你可以把它放在过滤器调用的init方法中 (3认同)

小智 20

这是一个足够古老的问题,但我会为那些喜欢我google这个问题的人添加我的答案.

您必须继承过滤器GenericFilterBean并将其标记为Spring@Component

@Component
public class MyFilter extends GenericFilterBean {

    @Autowired
    private MyComponent myComponent;

 //implementation

}
Run Code Online (Sandbox Code Playgroud)

然后在Spring上下文中注册它:

@Configuration
public class MyFilterConfigurerAdapter extends WebMvcConfigurerAdapter {

    @Autowired
    private MyFilter myFilter;

    @Bean
    public FilterRegistrationBean myFilterRegistrationBean() {
        FilterRegistrationBean regBean = new FilterRegistrationBean();
        regBean.setFilter(myFilter);
        regBean.setOrder(1);
        regBean.addUrlPatterns("/myFilteredURLPattern");

        return regBean;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样可以在过滤器中自动连接组件.

  • 此示例仅适用于Spring引导,因为"FilterRegistrationBean"仅存在于此处 (4认同)
  • 你刚刚钉了它.谢谢 (3认同)

Igo*_*ash 6

如果您的过滤器类扩展了 GenericFilterBean,您可以通过以下方式在应用程序上下文中获取对 bean 的引用:

public void initFilterBean() throws ServletException {

@Override
public void initFilterBean() throws ServletException {

        WebApplicationContext webApplicationContext =
            WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        //reference to bean from app context
        yourBeanToInject = webApplicationContext.getBean(yourBeanToInject.class);

        //do something with your bean
        propertyValue = yourBeanToInject.getValue("propertyName");
}
Run Code Online (Sandbox Code Playgroud)

对于那些不喜欢硬编码 bean 名称或需要将多个 bean 引用注入过滤器的人来说,这是一种不太明确的方法:

@Autowired
private YourBeanToInject yourBeanToInject;

@Override
public void initFilterBean() throws ServletException{

    SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, getServletContext());

    //do something with your bean
    propertyValue = yourBeanToInject.getValue("propertyName");
}
Run Code Online (Sandbox Code Playgroud)