无法在 Spring 中自动连接我的身份验证过滤器中的服务

Posted

技术标签:

【中文标题】无法在 Spring 中自动连接我的身份验证过滤器中的服务【英文标题】:Unable to autowire the service inside my authentication filter in Spring 【发布时间】:2015-12-06 07:10:38 【问题描述】:

我正在尝试通过令牌对用户进行身份验证,但是当我尝试在 AuthenticationTokenProcessingFilter 内自动连接我的服务时,我得到空指针异常。 因为 autowired 服务为空,我该如何解决这个问题?

我的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);
    

我尝试在我的AppConfig 中添加以下内容

@Bean(name="myServices")
    public MyServices stockService() 
        return new MyServiceImpl();
    

我的 AppConfig 注释是

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.marketplace")
public class AppConfig extends WebMvcConfigurerAdapter 

【问题讨论】:

您的 Bean MyServices 是在您查看日志时创建的吗?您可以尝试在您的MyServices 中添加@Component 是的,我尝试添加 @ComponentScan 并创建了服务! 您的 AppConfig 类是否有 @Configuration 注释? 除了@ComponentScan,您还需要将@Component 添加到AuthenticationTokenProcessingFilter @RahulYadav 我刚刚用我添加的注释更新了我的问题! 【参考方案1】:

我迟到了,但这个解决方案对我有用。

在 web.xml 中添加一个 ContextLoaderListener。 applicationContext 可以有依赖bean。

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

然后添加 MyFilter SpringBeanAutowiringSupport processInjectionBasedOnServletContext,它将 webapplicationcontext 添加到过滤器中,过滤器将添加所有依赖项。

@Component
public class MyFilter implements Filter 

    @Autowired
    @Qualifier("userSessionServiceImpl")
    private UserSessionService userSessionServiceImpl;

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain 
    chain) throws IOException, ServletException 
        HttpServletRequest httpRequest = (HttpServletRequest) req;
        if (userSessionServiceImpl == null) 
            ServletContext context = httpRequest.getSession().getServletContext();
        SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, context);
    

       .... (for brevity)
    

【讨论】:

【参考方案2】:

如果您的过滤器类扩展了 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");

对于那些不喜欢硬编码 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");

【讨论】:

【参考方案3】:

这是一个足够老的问题,但我会为喜欢我的人在谷歌这个问题上添加我的答案。

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

@Component
public class MyFilter extends GenericFilterBean 

    @Autowired
    private MyComponent myComponent;

 //implementation


然后在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;
    

这会正确地自动装配过滤器中的组件。

【讨论】:

你刚刚成功了。谢谢 此示例仅适用于 Spring boot,因为 FilterRegistrationBean 仅存在于那里 不适合我。 WebMvcConfigurerAdapter 现在也被弃用了【参考方案4】:

您不能从开箱即用的过滤器中使用依赖注入。尽管您使用的是 GenericFilterBean,但您的 Servlet 过滤器不是由 spring 管理的。正如 javadocs 所指出的

这个通用过滤器基类不依赖于 Spring org.springframework.context.ApplicationContext 概念。过滤器 通常不加载自己的上下文,而是访问服务 bean 来自 Spring 根应用程序上下文,可通过过滤器的 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 ...    
    


【讨论】:

@haim 为什么我在使用这个解决方案时会得到org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'mypackage.TokenAuthenticationService' available 听起来像是 TokenAuthenticationService 定义中的问题。应该在配置中使用 Bean 进行注释,或者在 XML 或 Service 中使用组件扫描等【参考方案5】:

我只是通过添加使其工作

SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);

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

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")) 

【讨论】:

您可能需要考虑将其添加到@PostConstruct 方法中,以防止它在每个 doFilter() 调用中执行。 你可以把这个放在过滤器调用的init方法中【参考方案6】:

您可以配置您的 bean 过滤器并作为参数传递您需要的任何内容。我知道过滤器所在的 Spring 上下文之外,您无法获得 spring 的自动扫描所做的依赖注入。但不是 100% 确定您是否可以在过滤器中添加一个花哨的注释来做一些神奇的事情

   <filter>
   <filter-name>YourFilter</filter-name>
       <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

<filter-mapping>
   <filter-name>YourFilter</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>

然后在spring.xml中注入bean

  <bean id="YourFilter" class="com.YourFilter">
     <property name="param">
        <value>values</value>
     </property>
  </bean>

【讨论】:

你能帮我用 Java 配置吗?我不擅长 xml 配置!! 第一个 xml 它在你的 web.xml(servlet conf) 上,第二个必须是你的 applicationContext.xml(Spring conf)。你有很多教程来解释如何配置你的框架

以上是关于无法在 Spring 中自动连接我的身份验证过滤器中的服务的主要内容,如果未能解决你的问题,请参考以下文章

使用摘要过滤器的 Spring-boot 摘要身份验证失败

没有客户端身份验证。尝试添加适当的身份验证过滤器异常 spring oauth2

如何在 Spring 身份验证管理器之前执行自定义处理程序

如何在 Swagger-ui 中显示 Spring Security 用户名密码身份验证过滤器 url?

无法在 Spring Boot 2.0.0 中自动装配身份验证管理器

在 Spring Boot 中成功身份验证后未生成 JWT 令牌