Arian
Arian

Reputation: 7749

Bean cannot be autowired in a class

I'm using JWT (Json Web Token) to secure my Spring boot application.

There's a class which doesn't accept any bean.

First, I thought maybe the bean I want to inject is not defined. So I decided to print the list of beans names using spring ApplicationContext. But I found out that even ApplicationContext can not be injected into this class:

Any idea why this happens ?

import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Arrays;

public class JWTLoginFilter extends AbstractAuthenticationProcessingFilter {

    @Autowired
    private TokenAuthenticationService tokenAuthenticationService;

    @Autowired
    private ApplicationContext applicationContext;

    public JWTLoginFilter(String url, AuthenticationManager authManager) {
        super(new AntPathRequestMatcher(url));
        setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest req,
            HttpServletResponse res) throws AuthenticationException,
            IOException, ServletException {

        CustomUserDetails creds = new ObjectMapper().readValue(
                req.getInputStream(), CustomUserDetails.class);

        return getAuthenticationManager().authenticate(
                new UsernamePasswordAuthenticationToken(creds.getUsername(),
                        creds.getPassword()));
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest req,
            HttpServletResponse res, FilterChain chain, Authentication auth) {
        java.util.List s = Arrays.asList(applicationContext.getBeanDefinitionNames());
        System.out.println(s);
        tokenAuthenticationService.addAuthentication(res, auth.getName());
    }
}

Upvotes: 1

Views: 841

Answers (2)

Arian
Arian

Reputation: 7749

Well, I finally decided to change how these classes are designed. I made methods inside TokenAuthenticationService static.

Upvotes: 0

Andrew
Andrew

Reputation: 49656

The JWTLoginFilter has to be a bean as well to allow Spring to inject other beans into. Currently, Spring doesn't have any control over this. Marking the class with the @Component / @Service / @Repository annotations (depends on what role your filter plays, I assume the @Component is a good choice) is going to resolve the issue.

EDIT 1:

JWTLoginFilter required a bean of type java.lang.String that could not be found. Can a component have a constructor?

The problem: Spring tried to use a two-argument constructor to create a bean and was expected that these two arguments are its beans. But it's not true because there is no bean with the String class.

The solution: You should define a non-argument constructor to allow Spring to make an untuned instance without issues. Then create setters to provide methods which Spring will use to inject needed dependencies.

EDIT 2:

The workaround is to define a String bean (in a @Configuration class) which will be injected into the JWTLoginFilter constructor, but I'm not sure that your filter needs to have some external dependencies.

@Bean
public String getStringPatternBean() {
    return "pattern";
}

Upvotes: 3

Related Questions