Unable to autowire the service inside my authentic

2020-01-29 03:35发布

I am trying to authenticate user by token, But when i try to auto wire one my services inside the AuthenticationTokenProcessingFilter i get null pointer exception. because autowired service is null , how can i fix this issue ?

My AuthenticationTokenProcessingFilter class

@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);
    }
}

I Tried adding follwing in my AppConfig

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

My AppConfig Annotations are

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

6条回答
唯我独甜
2楼-- · 2020-01-29 04:19

You cannot use dependency injection from a filter out of the box. Although you are using GenericFilterBean your Servlet Filter is not managed by spring. As noted by the javadocs

This generic filter base class has no dependency on the Spring org.springframework.context.ApplicationContext concept. Filters usually don't load their own context but rather access service beans from the Spring root application context, accessible via the filter's ServletContext (see org.springframework.web.context.support.WebApplicationContextUtils).

In plain English we cannot expect spring to inject the service, but we can lazy set it on the first call. E.g.

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 ...    
    }

}
查看更多
冷血范
3楼-- · 2020-01-29 04:30

I just made it work by adding

SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);

I am unsure why we should do this even when i tried adding explicit qualifier. and now the code looks like

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")) {
查看更多
神经病院院长
4楼-- · 2020-01-29 04:31

It's an old enough question, but I'll add my answer for those who like me google this issue.

You must inherit your filter from GenericFilterBean and mark it as a Spring @Component

@Component
public class MyFilter extends GenericFilterBean {

    @Autowired
    private MyComponent myComponent;

 //implementation

}

And then register it in Spring context:

@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;
    }
}

This properly autowires your components in the filter.

查看更多
手持菜刀,她持情操
5楼-- · 2020-01-29 04:32

If your filter class extends GenericFilterBean you can get a reference to a bean in your app context this way:

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

And here is less explicit way for those who doesn't like hardcoding bean names or need to inject more than one bean reference into the filter:

@Autowired
private YourBeanToInject yourBeanToInject;

@Override
public void initFilterBean() throws ServletException{

    SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, getServletContext());

    //do something with your bean
    propertyValue = yourBeanToInject.getValue("propertyName");
}
查看更多
做个烂人
6楼-- · 2020-01-29 04:33

I am late to the party but this solution worked for me.

Add a ContextLoaderListener in web.xml. applicationContext can have dependency beans.

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

Then add in MyFilter SpringBeanAutowiringSupport processInjectionBasedOnServletContext which will add the webapplicationcontext into the filter which will add all the dependencies.

@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)
    }

}

查看更多
祖国的老花朵
7楼-- · 2020-01-29 04:34

You can configure your bean filter and pass as a parameter whatever you need. I know out of Spring context where the filter it is, you cannot get the dependency injection that the auto-scan of spring does. But not 100% sure if there´s a fancy annotation that you can put in your filter to do some magic stuff

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

and then inject bean in the spring.xml

  <bean id="YourFilter" class="com.YourFilter">
     <property name="param">
        <value>values</value>
     </property>
  </bean>
查看更多
登录 后发表回答