Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@autowired is null in UsernamePasswordAuthenticationFilter Spring Boot [duplicate]

I am new to spring boot, I am trying to implement an authentication filter using spring security in my Spring boot app, but I do not understand why when using the @autowired annotation in the tokenService attribute it is null, can someone explain to me why this happens? and what is the correct configuration in java code (I'm not using xml files) so that spring the tokenService attribute is not null. next I'm going to show you the classes of interest:

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";

private AuthenticationManager authenticationManager;

@Autowired
private TokenService tokenService;//this is null

public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
    this.authenticationManager = authenticationManager;
}

@Override
public Authentication attemptAuthentication(HttpServletRequest req,
                                            HttpServletResponse res) throws AuthenticationException {
    try {
        LoginDto creds = new ObjectMapper()
                .readValue(req.getInputStream(), LoginDto.class);

        return authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        creds.getEmail(),
                        creds.getPassword(),
                        new ArrayList<>())
        );
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

@Override
protected void successfulAuthentication(HttpServletRequest req,
                                        HttpServletResponse res,
                                        FilterChain chain,
                                        Authentication auth) throws IOException, ServletException {
String token=tokenService.createAuthenticationToken(auth);//nullpointer exception here!
        long expirationTime=tokenService.getExpirationTime(token);
    res.addHeader("Access-Control-Expose-Headers", "Authorization");
        res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
    res.setHeader("Content-Type", "application/json");
        res.getWriter().print("{\"expiresIn\": "+expirationTime+"}");}}

and it's my TokenService class:

@Component
public class TokenService implements Serializable {

private static final long serialVersionUID = 1L;

long expirationTime = new Long(86400000); // 1 day
public static final String SECRET = "SecretKeyToGenJWTs";
public static final long EXPIRATION_TIME = 86_400_000; // 1 day
public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";

@Autowired
private UserService userService;

@Autowired
private ProviderService providerService;

public TokenService() {

}

public String createAuthenticationToken(Authentication auth) {
    String token = Jwts.builder().setSubject(((User) auth.getPrincipal()).getUsername()).setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + expirationTime))
            .signWith(SignatureAlgorithm.HS512, SECRET.getBytes()).compact();
    return token;
}

public long getExpirationTime(String token) {
    Claims claims = Jwts.parser().setSigningKey(SECRET.getBytes()).parseClaimsJws(token).getBody();
    Integer intDate = (Integer) claims.get("exp");
    long tokenDate = (intDate.longValue() * 1000);
    return tokenDate;
}

public String getSubjectFromAuthorizationToken(String token) {
    String subject = Jwts.parser().setSigningKey(SECRET.getBytes()).parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
            .getBody().getSubject();
    return subject;
}
}

And it's my config class:

@Configuration
@EnableWebSecurity
@ComponentScan({"com.espiritware.opusclick.security"})

public class WebSecurity extends WebSecurityConfigurerAdapter {

public static final String USER_REGISTRATION_URL = "/v1/users/registration";
public static final String PROVIDER_REGISTRATION_URL = "/v1/providers/registration";
public static final String CONFIRM_REGISTRATION_URL = "/v1/registrationConfirm";
public static final String SEND_RESET_PASSWORD_EMAIL = "/v1/sendResetPasswordEmail";
public static final String RESET_PASSWORD = "/v1/resetPassword";
public static final String LOGIN = "/v1/login";


private UserDetailsService userDetailsService;

private BCryptPasswordEncoder bCryptPasswordEncoder;

public WebSecurity(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
    this.userDetailsService = userDetailsService;
    this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and().csrf().disable()
            .authorizeRequests().antMatchers(HttpMethod.POST, USER_REGISTRATION_URL).permitAll()
            .antMatchers(HttpMethod.POST, PROVIDER_REGISTRATION_URL).permitAll()
            .antMatchers(HttpMethod.GET, CONFIRM_REGISTRATION_URL).permitAll()
            .antMatchers(HttpMethod.POST, SEND_RESET_PASSWORD_EMAIL).permitAll()
            .antMatchers(HttpMethod.POST, RESET_PASSWORD).permitAll()
            .anyRequest().authenticated().and()
            .addFilter(new JWTAuthenticationFilter(authenticationManager()))
            .addFilter(new JWTAuthorizationFilter(authenticationManager()))
            // this disables session creation on Spring Security
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}

@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}

@Bean
CorsConfigurationSource corsConfigurationSource() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
    return source;
}

}

Thanks!

like image 314
AlejoDev Avatar asked Jan 05 '18 04:01

AlejoDev


1 Answers

I too had similar problem while using the BasicAuthenticationFilter. Refer below solution which I employed to get things working.

Update the JWTAuthenticationFilter constructor to accept the application context as one of the parameters

public JWTAuthenticationFilter(AuthenticationManager authenticationManager, ApplicationContext ctx) {
    this.authenticationManager = authenticationManager;
    this.tokenService= ctx.getBean(TokenService.class);
}

Update the config to pass the ApplicationContext to the filter instance

@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
        // .. some settings
        .addFilter(new JWTAuthenticationFilter(authenticationManager(), getApplicationContext()))
        // some other settings    
}

Reason for this design approach - The Authentication processing mechanisms supported by Spring security is based on filters which extends GenericFilterBean. As per the docs (emphasis mine)

This generic filter base class has no dependency on the Spring 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 WebApplicationContextUtils).

Thus we need to supply the ApplicationContext to the filter to access the service bean(s).

Let know in comments if you need further information.

P.S.: You can consider using the JWTAuthenticationFilter as @Bean also and refer it in configuration as .addFilter(jwtAuthFilter())

@Bean
public JWTAuthenticationFilter jwtAuthFilter() throws Exception {
    return new JWTAuthenticationFilter(authenticationManager(), getApplicationContext());
}
like image 175
Bond - Java Bond Avatar answered Oct 01 '22 16:10

Bond - Java Bond