1

I have a custom UserDetailsService:

public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private AccountRepository accountRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;
    private static Logger logger = LoggerFactory.getLogger(JWTLoginFilter.class);

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        CustomUserDetails account = accountRepository.findByUsername(username);
        if (account != null) {
            return account;
        } else {
            throw new UsernameNotFoundException("could not find the user '" + username + "'");
        }
    }

    public void saveUser(String userName, String password) {
        CustomUserDetails userDetails = new CustomUserDetails(userName, passwordEncoder.encode(password), true, true, true,true, AuthorityUtils.commaSeparatedStringToAuthorityList("USER_ROLE"));
        accountRepository.save(userDetails);
        logger.debug("New user with username " + userName + " was created");
    }

}

and I have a sign up filter (which handles creating new users) and extends AbstractAuthenticationProcessingFilter:

public class JWTSignupFilter extends AbstractAuthenticationProcessingFilter {

    @Autowired
    private CustomUserDetailsService userDetailService;
    private static Logger logger = LoggerFactory.getLogger(JWTLoginFilter.class);

    public JWTSignupFilter(String url, AuthenticationManager authManager) {
        super(new AntPathRequestMatcher(url, HttpMethod.POST.toString()));
        setAuthenticationManager(authManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        CustomUserDetails creds = new ObjectMapper().readValue(request.getInputStream(), CustomUserDetails.class);
        if (userDetailService.loadUserByUsername(creds.getUsername()) != null) {
            logger.debug("Duplicate username " + creds.getUsername());
            throw new AuthenticationException("Duplicate username") {
                private static final long serialVersionUID = 1L;
            };
        }
        userDetailService.saveUser(creds.getUsername(), creds.getPassword());
        return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(creds.getUsername(),creds.getPassword()));
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication auth) throws IOException, ServletException {
        TokenAuthenticationService.addAuthentication(response, auth.getName());
        chain.doFilter(request, response);
    }
}

I get null pointer exception when the execution reaches userDetailService.loadUserByUsername, which means autowiring didn't work.

I tried implementing ApplicationContextAware like the following, but it is still Null. I also annotated JWTSignupFilter with @Service but it didn't work either. Any idea how to fix this issue ?

public class JWTSignupFilter extends AbstractAuthenticationProcessingFilter implements ApplicationContextAware {

    private CustomUserDetailsService userDetailService;

    .....

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        userDetailService = applicationContext.getBean(CustomUserDetailsService.class);     
    }
}

This is the overriden configure method in WebSecurityConfigurerAdapter, where login filter comes in to the play:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeRequests()
            .antMatchers("/login").permitAll()
        .and()
        .authorizeRequests()
            .antMatchers("/signup").permitAll()
        .and()
        .authorizeRequests()
            .anyRequest().authenticated()
        .and()
            .logout().logoutUrl("/logout").logoutSuccessHandler(logoutHandler).logoutSuccessUrl("/login").invalidateHttpSession(true)
        .and()
        // We filter the api/signup requests
        .addFilterBefore(
            new JWTSignupFilter("/signup", authenticationManager()),
            UsernamePasswordAuthenticationFilter.class)
        // We filter the api/login requests
        .addFilterBefore(
            new JWTLoginFilter("/login", authenticationManager()),
            UsernamePasswordAuthenticationFilter.class)
        // And filter other requests to check the presence of JWT in
        // header
        .addFilterBefore(
            new JWTAuthenticationFilter(userDetailsServiceBean()),
            UsernamePasswordAuthenticationFilter.class);
} 
Arian
  • 7,397
  • 21
  • 89
  • 177

1 Answers1

-1

Try this:

Add below code into your configuration file:

    @Bean
    public JWTSignupFilter jWTSignupFilter() throws Exception {
        return new JWTSignupFilter("/login", authenticationManager());
    }

Add below line into your WebSecurityConfigurerAdapter extended class

@Autowired 
JWTLoginFilter jWTSignupFilter

and replace

.addFilterBefore(
            new JWTLoginFilter("/login", authenticationManager()),
            UsernamePasswordAuthenticationFilter.class)

with

.addFilterBefore(
            jWTSignupFilter,
            UsernamePasswordAuthenticationFilter.class)

Updated

Your WebSecurityConfigurerAdapter extended class should look like so:

public Class CustomConfigurationClass extends WebSecurityConfigurerAdapter{
  @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/login").permitAll()
            .and()
            .authorizeRequests()
                .antMatchers("/signup").permitAll()
            .and()
            .authorizeRequests()
                .anyRequest().authenticated()
            .and()
                .logout().logoutUrl("/logout").logoutSuccessHandler(logoutHandler).logoutSuccessUrl("/login").invalidateHttpSession(true)
            .and()
            // We filter the api/signup requests
            .addFilterBefore(
                    jWTSignupFilter(),
                    UsernamePasswordAuthenticationFilter.class)
            // We filter the api/login requests
            .addFilterBefore(
                new JWTLoginFilter("/login", authenticationManager()),
                UsernamePasswordAuthenticationFilter.class)
            // And filter other requests to check the presence of JWT in
            // header
            .addFilterBefore(
                new JWTAuthenticationFilter(userDetailsServiceBean()),
                UsernamePasswordAuthenticationFilter.class);
    } 

    @Bean
    public JWTSignupFilter jWTSignupFilter() throws Exception {
        return new JWTSignupFilter("/signup", authenticationManager());
    }
}
Afridi
  • 6,753
  • 2
  • 18
  • 27
  • I get this error: The dependencies of some of the beans in the application context form a cycle: webSecurityConfig (field private com.boot.cut_costs.security.auth.jwt.JWTSignupFilter com.boot.cut_costs.security.config.WebSecurityConfig.jWTSignupFilter) -> jWTSignupFilter – Arian May 11 '17 at 06:01
  • there is bean definition instantiation issue. try to add above bean definition into your WebSecurityConfigurerAdapter extended child class, and remove @Autowired JWTLoginFilter jWTSignupFilter and use these lines .addFilterBefore( jWTSignupFilter(), UsernamePasswordAuthenticationFilter.class) – Afridi May 11 '17 at 06:06
  • Then it's not using the bean anymore. It is instantiating it using java new keyword. – Arian May 11 '17 at 06:22
  • check updated answer, jWTSignupFilter() is annotated with @Bean, so it will work as bean – Afridi May 11 '17 at 06:32
  • this just makes the `JWTSignupFilter` an injectable bean, but (as @ArianHosseinzadeh points out) doesn't solve the OP's problem of injecting services into the `JWTSignupFilter`. – Mario Mar 22 '18 at 02:21